Landtiger LPC1768 C BigLib 1
A self made, custom C library for the LandTiger board.
 
Loading...
Searching...
No Matches
cl_prioqueue.c
Go to the documentation of this file.
1#include "cl_prioqueue.h"
2
3#include <alloca.h>
4#include <stdlib.h>
5#include <string.h>
6
7// TYPE DEFINITION
8
16
17#define RESZ_FACTOR 2
18#define BASE_CAPACITY 10
19
20// PRIVATE FUNCTIONS
21
22// The priority queue can be visualized as a binary tree, where the parent node has only
23// two children (left, right). These macros help to navigate through the tree (which is
24// acutually linear, i.e. an array).
25#define PARENT(i) ((i - 1) / 2) // The parent of the node at index i. E.g. for i = 0, parent = 0.
26#define LEFT(i) (2 * i + 1) // The left child of the node at index i. E.g. for i = 0, left = 1.
27#define RIGHT(i) (2 * i + 2) // The right child of the node at index i. E.g. for i = 0, right = 2.
28
29_PRIVATE inline void pSwap(void *const a, void *const b, u32 size)
30{
31 void *temp = alloca(size);
32 memcpy(temp, a, size);
33 memcpy(a, b, size);
34 memcpy(b, temp, size);
35}
36
37// When adding an element into the queue, it gets added at the end of the array.
38// If the element has a higher priority than its parent node, it needs to be bubbled
39// up until it reaches the correct position (i.e. the parent node has a HIGHER priority).
41{
42 if (!pq)
43 return;
44
45 u32 new_idx = pq->size - 1;
46 u8 *parent_elem, *new_elem;
47 while (new_idx > 0)
48 {
49 parent_elem = (u8 *)(pq->data) + (PARENT(new_idx) * pq->elem_size);
50 new_elem = (u8 *)(pq->data) + (new_idx * pq->elem_size);
51 if (pq->compare_fn(new_elem, parent_elem) < 0)
52 pSwap(parent_elem, new_elem, pq->elem_size);
53
54 new_idx = PARENT(new_idx); // Moving up the tree.
55 }
56}
57
58// When removing an element from the queue, the first element (i.e. the root of the tree)
59// gets removed. After that, the last element of the array gets moved to the root. This may
60// break the heap property, so we need to bubble down the new root until the heap property is restored.
62{
63 if (!pq)
64 return;
65
66 u32 idx = 0, smallest_idx = 0;
67 u8 *root_elem, *left_elem, *smallest_elem;
68
69 // Comparing the root with its children. If the root has a lower priority
70 // than one of its children, we need to swap them and continue bubbling down.
71 // The parent node is the first element of the array, i.e. at index 0. The left
72 // one follows at index 1, and the right one at index 2, etc. We iterate until
73 // we reach the leaf nodes (i.e. the last level of the tree). Leaf nodes DO NOT
74 // have children, so LEFT (and RIGHT) will be out of bounds. Since LEFT is lower
75 // than RIGHT, we use it as stopping condition.
76 while (LEFT(idx) < pq->size)
77 {
78 root_elem = (u8 *)(pq->data) + (idx * pq->elem_size);
79 left_elem = (u8 *)(pq->data) + (LEFT(idx) * pq->elem_size);
80 smallest_idx = idx;
81
82 // If the left child has a higher priority than the root
83 if (pq->compare_fn(left_elem, root_elem) < 0)
84 smallest_idx = LEFT(idx);
85
86 // Comparing the current smallest (could be the root or the left child) with the right child.
87 if (RIGHT(idx) < pq->size && pq->compare_fn((u8 *)(pq->data) + (RIGHT(idx) * pq->elem_size),
88 (u8 *)(pq->data) + (smallest_idx * pq->elem_size)) < 0)
89 smallest_idx = RIGHT(idx);
90
91 // If the root has a higher priority than both children
92 if (smallest_idx == idx)
93 break; // We're done.
94
95 // Swapping the root with the child that has a higher priority.
96 smallest_elem = (u8 *)(pq->data) + (smallest_idx * pq->elem_size);
97 pSwap(root_elem, smallest_elem, pq->elem_size);
98 idx = smallest_idx; // Moving down the tree.
99 }
100}
101
102_PRIVATE inline void *realloc_data(CL_PQueue *const pq, u32 new_capacity)
103{
104 void *new_data = MEM_Realloc(pq->allocator, pq->data, new_capacity * pq->elem_size);
105 if (!new_data)
106 return NULL;
107
108 pq->data = new_data;
109 pq->capacity = new_capacity;
110 return new_data;
111}
112
113// PUBLIC FUNCTIONS
114
116 CL_PQueueCompareFn compare_fn)
117{
118 if (!alloc || !capacity || !elem_sz || !compare_fn)
119 return NULL;
120
121 CL_PQueue *const pq = MEM_Alloc(alloc, sizeof(CL_PQueue));
122 if (!pq)
123 return NULL;
124
125 *pq = (CL_PQueue){
126 .data = MEM_Alloc(alloc, capacity * elem_sz),
127 .size = 0,
128 .capacity = capacity,
129 .elem_size = elem_sz,
130 .compare_fn = compare_fn,
131 .allocator = alloc,
132 };
133
134 if (!pq->data)
135 {
136 MEM_Free(alloc, pq);
137 return NULL;
138 }
139
140 return pq;
141}
142
144{
145 return CL_PQueueAllocWithCapacity(alloc, BASE_CAPACITY, elem_sz, compare_fn);
146}
147
149{
150 if (!pq)
151 return;
152
153 MEM_Free(pq->allocator, pq->data);
154 MEM_Free(pq->allocator, pq);
155}
156
157CL_Error CL_PQueueEnqueue(CL_PQueue *const pq, const void *const elem)
158{
159 if (!pq || !elem)
161
162 if (pq->size >= pq->capacity)
163 {
164 const u32 new_capacity = pq->capacity * RESZ_FACTOR;
165 if (!realloc_data(pq, new_capacity))
166 return CL_ERR_NO_MEMORY;
167 }
168
169 // Adding the element at the end (queues are FIFO)
170 const u32 offset = pq->size * pq->elem_size;
171 memcpy((u8 *)(pq->data) + offset, elem, pq->elem_size);
172 pq->size++;
173
174 // Bubbling up the new element to its correct position.
175 bubble_up_leaf(pq);
176 return CL_ERR_OK;
177}
178
179CL_Error CL_PQueueDequeue(CL_PQueue *const pq, void *out_elem)
180{
181 if (!pq || !out_elem)
183
184 if (!pq->size)
185 return CL_ERR_EMPTY;
186
187 // Getting the element with the highest priority (i.e. the root of the tree).
188 memcpy(out_elem, pq->data, pq->elem_size);
189 pq->size--;
190
191 // Moving the last element to the root and bubbling it down.
192 const u32 last_elem_offset = pq->size * pq->elem_size;
193 memcpy(pq->data, (u8 *)(pq->data) + last_elem_offset, pq->elem_size);
195 return CL_ERR_OK;
196}
197
198CL_Error CL_PQueuePeek(const CL_PQueue *const pq, void *out_elem)
199{
200 if (!pq || !out_elem)
202
203 if (!pq->size)
204 return CL_ERR_EMPTY;
205
206 // Getting the element with the highest priority (i.e. the root of the tree).
207 memcpy(out_elem, pq->data, pq->elem_size);
208 return CL_ERR_OK;
209}
210
212{
213 return pq ? pq->size : 0;
214}
215
216bool CL_PQueueIsEmpty(const CL_PQueue *const pq)
217{
218 return pq ? pq->size == 0 : true;
219}
220
221/*
222// ARRAY
223
224void *CL_PQueueAsArray(const CL_PQueue *const pq)
225{
226 return pq ? pq->data : NULL;
227}
228*/
void MEM_Free(MEM_Allocator *ma, void *ptr)
Free a previously allocated block.
Definition allocator.c:98
void * MEM_Alloc(MEM_Allocator *ma, u32 size)
Allocate a block of memory from the pool with at least 'size' bytes.
Definition allocator.c:52
void * MEM_Realloc(MEM_Allocator *ma, void *ptr, u32 new_size)
Reallocate a previously allocated block to a new size.
Definition allocator.c:131
#define RIGHT(i)
_PRIVATE void bubble_up_leaf(CL_PQueue *const pq)
#define LEFT(i)
bool CL_PQueueIsEmpty(const CL_PQueue *const pq)
Checks if the priority queue is empty.
void CL_PQueueFree(CL_PQueue *const pq)
Frees the memory previously assigned to the priority queue.
_PRIVATE void * realloc_data(CL_PQueue *const pq, u32 new_capacity)
CL_Error CL_PQueuePeek(const CL_PQueue *const pq, void *out_elem)
Gets the element with the highest priority from the priority queue without removing it.
#define BASE_CAPACITY
CL_Error CL_PQueueDequeue(CL_PQueue *const pq, void *out_elem)
Removes the element with the highest priority from the priority queue.
#define PARENT(i)
u32 CL_PQueueSize(const CL_PQueue *const pq)
Gets the number of elements in the priority queue.
_PRIVATE void bubble_down_root(CL_PQueue *const pq)
_PRIVATE void pSwap(void *const a, void *const b, u32 size)
#define RESZ_FACTOR
CL_PQueue * CL_PQueueAlloc(MEM_Allocator *const alloc, u32 elem_sz, CL_PQueueCompareFn compare_fn)
Initializes a priority queue with a default capacity.
CL_PQueue * CL_PQueueAllocWithCapacity(MEM_Allocator *const alloc, u32 capacity, u32 elem_sz, CL_PQueueCompareFn compare_fn)
Initializes a priority queue with the allocated memory provided by the user.
CL_Error CL_PQueueEnqueue(CL_PQueue *const pq, const void *const elem)
Adds an element to the priority queue.
int(* CL_PQueueCompareFn)(const void *, const void *)
struct __PQueue CL_PQueue
CL_Error
Specify this in the size parameter of the collections initialization to tell the library that the num...
Definition cl_types.h:24
@ CL_ERR_EMPTY
Empty collection.
Definition cl_types.h:41
@ CL_ERR_NO_MEMORY
No memory available.
Definition cl_types.h:35
@ CL_ERR_INVALID_PARAMS
Invalid arguments.
Definition cl_types.h:29
@ CL_ERR_OK
No error.
Definition cl_types.h:26
void * data
MEM_Allocator * allocator
The memory allocator.
CL_PQueueCompareFn compare_fn
The comparison function.
u32 elem_size
uint8_t u8
Definition types.h:8
#define _PRIVATE
Definition types.h:37
uint32_t u32
Definition types.h:6