chiark / gitweb /
fd51e0587cbda4bc29178750845bf0efa3cc915e
[elogind.git] / src / basic / prioq.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   Copyright 2013 Lennart Poettering
4 ***/
5
6 /*
7  * Priority Queue
8  * The prioq object implements a priority queue. That is, it orders objects by
9  * their priority and allows O(1) access to the object with the highest
10  * priority. Insertion and removal are Θ(log n). Optionally, the caller can
11  * provide a pointer to an index which will be kept up-to-date by the prioq.
12  *
13  * The underlying algorithm used in this implementation is a Heap.
14  */
15
16 #include <errno.h>
17 #include <stdlib.h>
18
19 #include "alloc-util.h"
20 #include "hashmap.h"
21 #include "prioq.h"
22
23 struct prioq_item {
24         void *data;
25         unsigned *idx;
26 };
27
28 struct Prioq {
29         compare_func_t compare_func;
30         unsigned n_items, n_allocated;
31
32         struct prioq_item *items;
33 };
34
35 Prioq *prioq_new(compare_func_t compare_func) {
36         Prioq *q;
37
38         q = new0(Prioq, 1);
39         if (!q)
40                 return q;
41
42         q->compare_func = compare_func;
43         return q;
44 }
45
46 Prioq* prioq_free(Prioq *q) {
47         if (!q)
48                 return NULL;
49
50         free(q->items);
51         return mfree(q);
52 }
53
54 int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
55         assert(q);
56
57         if (*q)
58                 return 0;
59
60         *q = prioq_new(compare_func);
61         if (!*q)
62                 return -ENOMEM;
63
64         return 0;
65 }
66
67 static void swap(Prioq *q, unsigned j, unsigned k) {
68         void *saved_data;
69         unsigned *saved_idx;
70
71         assert(q);
72         assert(j < q->n_items);
73         assert(k < q->n_items);
74
75         assert(!q->items[j].idx || *(q->items[j].idx) == j);
76         assert(!q->items[k].idx || *(q->items[k].idx) == k);
77
78         saved_data = q->items[j].data;
79         saved_idx = q->items[j].idx;
80         q->items[j].data = q->items[k].data;
81         q->items[j].idx = q->items[k].idx;
82         q->items[k].data = saved_data;
83         q->items[k].idx = saved_idx;
84
85         if (q->items[j].idx)
86                 *q->items[j].idx = j;
87
88         if (q->items[k].idx)
89                 *q->items[k].idx = k;
90 }
91
92 static unsigned shuffle_up(Prioq *q, unsigned idx) {
93         assert(q);
94
95         while (idx > 0) {
96                 unsigned k;
97
98                 k = (idx-1)/2;
99
100                 if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
101                         break;
102
103                 swap(q, idx, k);
104                 idx = k;
105         }
106
107         return idx;
108 }
109
110 static unsigned shuffle_down(Prioq *q, unsigned idx) {
111         assert(q);
112
113         for (;;) {
114                 unsigned j, k, s;
115
116                 k = (idx+1)*2; /* right child */
117                 j = k-1;       /* left child */
118
119                 if (j >= q->n_items)
120                         break;
121
122                 if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
123
124                         /* So our left child is smaller than we are, let's
125                          * remember this fact */
126                         s = j;
127                 else
128                         s = idx;
129
130                 if (k < q->n_items &&
131                     q->compare_func(q->items[k].data, q->items[s].data) < 0)
132
133                         /* So our right child is smaller than we are, let's
134                          * remember this fact */
135                         s = k;
136
137                 /* s now points to the smallest of the three items */
138
139                 if (s == idx)
140                         /* No swap necessary, we're done */
141                         break;
142
143                 swap(q, idx, s);
144                 idx = s;
145         }
146
147         return idx;
148 }
149
150 int prioq_put(Prioq *q, void *data, unsigned *idx) {
151         struct prioq_item *i;
152         unsigned k;
153
154         assert(q);
155
156         if (q->n_items >= q->n_allocated) {
157                 unsigned n;
158                 struct prioq_item *j;
159
160                 n = MAX((q->n_items+1) * 2, 16u);
161                 j = reallocarray(q->items, n, sizeof(struct prioq_item));
162                 if (!j)
163                         return -ENOMEM;
164
165                 q->items = j;
166                 q->n_allocated = n;
167         }
168
169         k = q->n_items++;
170         i = q->items + k;
171         i->data = data;
172         i->idx = idx;
173
174         if (idx)
175                 *idx = k;
176
177         shuffle_up(q, k);
178
179         return 0;
180 }
181
182 static void remove_item(Prioq *q, struct prioq_item *i) {
183         struct prioq_item *l;
184
185         assert(q);
186         assert(i);
187
188         l = q->items + q->n_items - 1;
189
190         if (i == l)
191                 /* Last entry, let's just remove it */
192                 q->n_items--;
193         else {
194                 unsigned k;
195
196                 /* Not last entry, let's replace the last entry with
197                  * this one, and reshuffle */
198
199                 k = i - q->items;
200
201                 i->data = l->data;
202                 i->idx = l->idx;
203                 if (i->idx)
204                         *i->idx = k;
205                 q->n_items--;
206
207                 k = shuffle_down(q, k);
208                 shuffle_up(q, k);
209         }
210 }
211
212 _pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
213         struct prioq_item *i;
214
215         assert(q);
216
217         if (idx) {
218                 if (*idx == PRIOQ_IDX_NULL ||
219                     *idx > q->n_items)
220                         return NULL;
221
222                 i = q->items + *idx;
223                 if (i->data != data)
224                         return NULL;
225
226                 return i;
227         } else {
228                 for (i = q->items; i < q->items + q->n_items; i++)
229                         if (i->data == data)
230                                 return i;
231                 return NULL;
232         }
233 }
234
235 int prioq_remove(Prioq *q, void *data, unsigned *idx) {
236         struct prioq_item *i;
237
238         if (!q)
239                 return 0;
240
241         i = find_item(q, data, idx);
242         if (!i)
243                 return 0;
244
245         remove_item(q, i);
246         return 1;
247 }
248
249 int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
250         struct prioq_item *i;
251         unsigned k;
252
253         assert(q);
254
255         i = find_item(q, data, idx);
256         if (!i)
257                 return 0;
258
259         k = i - q->items;
260         k = shuffle_down(q, k);
261         shuffle_up(q, k);
262         return 1;
263 }
264
265 void *prioq_peek(Prioq *q) {
266
267         if (!q)
268                 return NULL;
269
270         if (q->n_items <= 0)
271                 return NULL;
272
273         return q->items[0].data;
274 }
275
276 void *prioq_pop(Prioq *q) {
277         void *data;
278
279         if (!q)
280                 return NULL;
281
282         if (q->n_items <= 0)
283                 return NULL;
284
285         data = q->items[0].data;
286         remove_item(q, q->items);
287         return data;
288 }
289
290 unsigned prioq_size(Prioq *q) {
291
292         if (!q)
293                 return 0;
294
295         return q->n_items;
296 }
297
298 bool prioq_isempty(Prioq *q) {
299
300         if (!q)
301                 return true;
302
303         return q->n_items <= 0;
304 }