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