1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 * The prioq object implements a priority queue. That is, it orders objects by
6 * their priority and allows O(1) access to the object with the highest
7 * priority. Insertion and removal are Θ(log n). Optionally, the caller can
8 * provide a pointer to an index which will be kept up-to-date by the prioq.
10 * The underlying algorithm used in this implementation is a Heap.
13 #include "alloc-util.h"
22 compare_func_t compare_func
;
24 struct prioq_item
*items
;
27 Prioq
* prioq_new(compare_func_t compare_func
) {
35 .compare_func
= compare_func
,
41 Prioq
* prioq_free(Prioq
*q
) {
45 /* Invalidate the index fields of any remaining objects */
46 FOREACH_ARRAY(item
, q
->items
, q
->n_items
)
48 *(item
->idx
) = PRIOQ_IDX_NULL
;
54 int prioq_ensure_allocated(Prioq
**q
, compare_func_t compare_func
) {
58 assert((*q
)->compare_func
== compare_func
);
62 *q
= prioq_new(compare_func
);
69 static void swap(Prioq
*q
, unsigned j
, unsigned k
) {
71 assert(j
< q
->n_items
);
72 assert(k
< q
->n_items
);
74 assert(!q
->items
[j
].idx
|| *(q
->items
[j
].idx
) == j
);
75 assert(!q
->items
[k
].idx
|| *(q
->items
[k
].idx
) == k
);
77 SWAP_TWO(q
->items
[j
].data
, q
->items
[k
].data
);
78 SWAP_TWO(q
->items
[j
].idx
, q
->items
[k
].idx
);
87 static unsigned shuffle_up(Prioq
*q
, unsigned idx
) {
89 assert(idx
< q
->n_items
);
96 if (q
->compare_func(q
->items
[k
].data
, q
->items
[idx
].data
) <= 0)
106 static unsigned shuffle_down(Prioq
*q
, unsigned idx
) {
112 k
= (idx
+1)*2; /* right child */
113 j
= k
-1; /* left child */
118 if (q
->compare_func(q
->items
[j
].data
, q
->items
[idx
].data
) < 0)
120 /* So our left child is smaller than we are, let's
121 * remember this fact */
126 if (k
< q
->n_items
&&
127 q
->compare_func(q
->items
[k
].data
, q
->items
[s
].data
) < 0)
129 /* So our right child is smaller than we are, let's
130 * remember this fact */
133 /* s now points to the smallest of the three items */
136 /* No swap necessary, we're done */
146 int prioq_put(Prioq
*q
, void *data
, unsigned *idx
) {
151 if (!GREEDY_REALLOC(q
->items
, MAX(q
->n_items
+ 1, 16u)))
155 q
->items
[k
] = (struct prioq_item
) {
168 int _prioq_ensure_put(Prioq
**q
, compare_func_t compare_func
, void *data
, unsigned *idx
) {
171 r
= prioq_ensure_allocated(q
, compare_func
);
175 return prioq_put(*q
, data
, idx
);
178 static void remove_item(Prioq
*q
, struct prioq_item
*i
) {
179 struct prioq_item
*l
;
184 /* Let's invalidate the index pointer stored in the user's object to indicate the item is now removed
185 * from the priority queue */
187 *(i
->idx
) = PRIOQ_IDX_NULL
;
189 l
= q
->items
+ q
->n_items
- 1;
192 /* Last entry, let's just remove it */
197 /* Not last entry, let's replace the last entry with
198 * this one, and reshuffle */
200 assert(i
>= q
->items
);
209 k
= shuffle_down(q
, k
);
214 static struct prioq_item
* find_item(Prioq
*q
, void *data
, unsigned *idx
) {
215 struct prioq_item
*i
;
223 if (*idx
== PRIOQ_IDX_NULL
||
233 for (i
= q
->items
; i
< q
->items
+ q
->n_items
; i
++)
240 int prioq_remove(Prioq
*q
, void *data
, unsigned *idx
) {
241 struct prioq_item
*i
;
246 i
= find_item(q
, data
, idx
);
254 void prioq_reshuffle(Prioq
*q
, void *data
, unsigned *idx
) {
255 struct prioq_item
*i
;
260 i
= find_item(q
, data
, idx
);
264 assert(i
>= q
->items
);
266 k
= shuffle_down(q
, k
);
270 void* prioq_peek_by_index(Prioq
*q
, unsigned idx
) {
274 if (idx
>= q
->n_items
)
277 return q
->items
[idx
].data
;
280 void* prioq_pop(Prioq
*q
) {
289 data
= q
->items
[0].data
;
290 remove_item(q
, q
->items
);
294 unsigned prioq_size(Prioq
*q
) {
302 bool prioq_isempty(Prioq
*q
) {
307 return q
->n_items
<= 0;