]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/prioq.c
Merge pull request #653 from dvdhrm/bus-gold
[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 free(q);
66
67 return NULL;
68 }
69
70 int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
71 assert(q);
72
73 if (*q)
74 return 0;
75
76 *q = prioq_new(compare_func);
77 if (!*q)
78 return -ENOMEM;
79
80 return 0;
81 }
82
83 static void swap(Prioq *q, unsigned j, unsigned k) {
84 void *saved_data;
85 unsigned *saved_idx;
86
87 assert(q);
88 assert(j < q->n_items);
89 assert(k < q->n_items);
90
91 assert(!q->items[j].idx || *(q->items[j].idx) == j);
92 assert(!q->items[k].idx || *(q->items[k].idx) == k);
93
94 saved_data = q->items[j].data;
95 saved_idx = q->items[j].idx;
96 q->items[j].data = q->items[k].data;
97 q->items[j].idx = q->items[k].idx;
98 q->items[k].data = saved_data;
99 q->items[k].idx = saved_idx;
100
101 if (q->items[j].idx)
102 *q->items[j].idx = j;
103
104 if (q->items[k].idx)
105 *q->items[k].idx = k;
106 }
107
108 static unsigned shuffle_up(Prioq *q, unsigned idx) {
109 assert(q);
110
111 while (idx > 0) {
112 unsigned k;
113
114 k = (idx-1)/2;
115
116 if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
117 break;
118
119 swap(q, idx, k);
120 idx = k;
121 }
122
123 return idx;
124 }
125
126 static unsigned shuffle_down(Prioq *q, unsigned idx) {
127 assert(q);
128
129 for (;;) {
130 unsigned j, k, s;
131
132 k = (idx+1)*2; /* right child */
133 j = k-1; /* left child */
134
135 if (j >= q->n_items)
136 break;
137
138 if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
139
140 /* So our left child is smaller than we are, let's
141 * remember this fact */
142 s = j;
143 else
144 s = idx;
145
146 if (k < q->n_items &&
147 q->compare_func(q->items[k].data, q->items[s].data) < 0)
148
149 /* So our right child is smaller than we are, let's
150 * remember this fact */
151 s = k;
152
153 /* s now points to the smallest of the three items */
154
155 if (s == idx)
156 /* No swap necessary, we're done */
157 break;
158
159 swap(q, idx, s);
160 idx = s;
161 }
162
163 return idx;
164 }
165
166 int prioq_put(Prioq *q, void *data, unsigned *idx) {
167 struct prioq_item *i;
168 unsigned k;
169
170 assert(q);
171
172 if (q->n_items >= q->n_allocated) {
173 unsigned n;
174 struct prioq_item *j;
175
176 n = MAX((q->n_items+1) * 2, 16u);
177 j = realloc(q->items, sizeof(struct prioq_item) * n);
178 if (!j)
179 return -ENOMEM;
180
181 q->items = j;
182 q->n_allocated = n;
183 }
184
185 k = q->n_items++;
186 i = q->items + k;
187 i->data = data;
188 i->idx = idx;
189
190 if (idx)
191 *idx = k;
192
193 shuffle_up(q, k);
194
195 return 0;
196 }
197
198 static void remove_item(Prioq *q, struct prioq_item *i) {
199 struct prioq_item *l;
200
201 assert(q);
202 assert(i);
203
204 l = q->items + q->n_items - 1;
205
206 if (i == l)
207 /* Last entry, let's just remove it */
208 q->n_items--;
209 else {
210 unsigned k;
211
212 /* Not last entry, let's replace the last entry with
213 * this one, and reshuffle */
214
215 k = i - q->items;
216
217 i->data = l->data;
218 i->idx = l->idx;
219 if (i->idx)
220 *i->idx = k;
221 q->n_items--;
222
223 k = shuffle_down(q, k);
224 shuffle_up(q, k);
225 }
226 }
227
228 _pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
229 struct prioq_item *i;
230
231 assert(q);
232
233 if (idx) {
234 if (*idx == PRIOQ_IDX_NULL ||
235 *idx > q->n_items)
236 return NULL;
237
238 i = q->items + *idx;
239 if (i->data != data)
240 return NULL;
241
242 return i;
243 } else {
244 for (i = q->items; i < q->items + q->n_items; i++)
245 if (i->data == data)
246 return i;
247 return NULL;
248 }
249 }
250
251 int prioq_remove(Prioq *q, void *data, unsigned *idx) {
252 struct prioq_item *i;
253
254 if (!q)
255 return 0;
256
257 i = find_item(q, data, idx);
258 if (!i)
259 return 0;
260
261 remove_item(q, i);
262 return 1;
263 }
264
265 int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
266 struct prioq_item *i;
267 unsigned k;
268
269 assert(q);
270
271 i = find_item(q, data, idx);
272 if (!i)
273 return 0;
274
275 k = i - q->items;
276 k = shuffle_down(q, k);
277 shuffle_up(q, k);
278 return 1;
279 }
280
281 void *prioq_peek(Prioq *q) {
282
283 if (!q)
284 return NULL;
285
286 if (q->n_items <= 0)
287 return NULL;
288
289 return q->items[0].data;
290 }
291
292 void *prioq_pop(Prioq *q) {
293 void *data;
294
295 if (!q)
296 return NULL;
297
298 if (q->n_items <= 0)
299 return NULL;
300
301 data = q->items[0].data;
302 remove_item(q, q->items);
303 return data;
304 }
305
306 unsigned prioq_size(Prioq *q) {
307
308 if (!q)
309 return 0;
310
311 return q->n_items;
312 }
313
314 bool prioq_isempty(Prioq *q) {
315
316 if (!q)
317 return true;
318
319 return q->n_items <= 0;
320 }