chiark / gitweb /
Prep v231: Move elogind specific code in login/logind.c to login/elogind.c
[elogind.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 }