chiark / gitweb /
manager: notify plymouth about progress if it is running
[elogind.git] / src / hashmap.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include "util.h"
28 #include "hashmap.h"
29 #include "macro.h"
30
31 #define NBUCKETS 127
32
33 struct hashmap_entry {
34         const void *key;
35         void *value;
36         struct hashmap_entry *bucket_next, *bucket_previous;
37         struct hashmap_entry *iterate_next, *iterate_previous;
38 };
39
40 struct Hashmap {
41         hash_func_t hash_func;
42         compare_func_t compare_func;
43
44         struct hashmap_entry *iterate_list_head, *iterate_list_tail;
45         unsigned n_entries;
46 };
47
48 #define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
49
50 unsigned string_hash_func(const void *p) {
51         unsigned hash = 0;
52         const char *c;
53
54         for (c = p; *c; c++)
55                 hash = 31 * hash + (unsigned) *c;
56
57         return hash;
58 }
59
60 int string_compare_func(const void *a, const void *b) {
61         return strcmp(a, b);
62 }
63
64 unsigned trivial_hash_func(const void *p) {
65         return PTR_TO_UINT(p);
66 }
67
68 int trivial_compare_func(const void *a, const void *b) {
69         return a < b ? -1 : (a > b ? 1 : 0);
70 }
71
72 Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
73         Hashmap *h;
74
75         if (!(h = malloc0(ALIGN(sizeof(Hashmap)) + NBUCKETS * ALIGN(sizeof(struct hashmap_entry*)))))
76                 return NULL;
77
78         h->hash_func = hash_func ? hash_func : trivial_hash_func;
79         h->compare_func = compare_func ? compare_func : trivial_compare_func;
80
81         h->n_entries = 0;
82         h->iterate_list_head = h->iterate_list_tail = NULL;
83
84         return h;
85 }
86
87 int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
88         assert(h);
89
90         if (*h)
91                 return 0;
92
93         if (!(*h = hashmap_new(hash_func, compare_func)))
94                 return -ENOMEM;
95
96         return 0;
97 }
98
99 static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
100         assert(h);
101         assert(e);
102
103         /* Insert into hash table */
104         e->bucket_next = BY_HASH(h)[hash];
105         e->bucket_previous = NULL;
106         if (BY_HASH(h)[hash])
107                 BY_HASH(h)[hash]->bucket_previous = e;
108         BY_HASH(h)[hash] = e;
109
110         /* Insert into iteration list */
111         e->iterate_previous = h->iterate_list_tail;
112         e->iterate_next = NULL;
113         if (h->iterate_list_tail) {
114                 assert(h->iterate_list_head);
115                 h->iterate_list_tail->iterate_next = e;
116         } else {
117                 assert(!h->iterate_list_head);
118                 h->iterate_list_head = e;
119         }
120         h->iterate_list_tail = e;
121
122         h->n_entries++;
123         assert(h->n_entries >= 1);
124 }
125
126 static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
127         assert(h);
128         assert(e);
129
130         /* Remove from iteration list */
131         if (e->iterate_next)
132                 e->iterate_next->iterate_previous = e->iterate_previous;
133         else
134                 h->iterate_list_tail = e->iterate_previous;
135
136         if (e->iterate_previous)
137                 e->iterate_previous->iterate_next = e->iterate_next;
138         else
139                 h->iterate_list_head = e->iterate_next;
140
141         /* Remove from hash table bucket list */
142         if (e->bucket_next)
143                 e->bucket_next->bucket_previous = e->bucket_previous;
144
145         if (e->bucket_previous)
146                 e->bucket_previous->bucket_next = e->bucket_next;
147         else
148                 BY_HASH(h)[hash] = e->bucket_next;
149
150         assert(h->n_entries >= 1);
151         h->n_entries--;
152 }
153
154 static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
155         unsigned hash;
156
157         assert(h);
158         assert(e);
159
160         hash = h->hash_func(e->key) % NBUCKETS;
161
162         unlink_entry(h, e, hash);
163         free(e);
164 }
165
166 void hashmap_free(Hashmap*h) {
167
168         if (!h)
169                 return;
170
171         hashmap_clear(h);
172
173         free(h);
174 }
175
176 void hashmap_free_free(Hashmap *h) {
177         void *p;
178
179         while ((p = hashmap_steal_first(h)))
180                 free(p);
181
182         hashmap_free(h);
183 }
184
185 void hashmap_clear(Hashmap *h) {
186         if (!h)
187                 return;
188
189         while (h->iterate_list_head)
190                 remove_entry(h, h->iterate_list_head);
191 }
192
193 static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
194         struct hashmap_entry *e;
195         assert(h);
196         assert(hash < NBUCKETS);
197
198         for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
199                 if (h->compare_func(e->key, key) == 0)
200                         return e;
201
202         return NULL;
203 }
204
205 int hashmap_put(Hashmap *h, const void *key, void *value) {
206         struct hashmap_entry *e;
207         unsigned hash;
208
209         assert(h);
210
211         hash = h->hash_func(key) % NBUCKETS;
212
213         if ((e = hash_scan(h, hash, key))) {
214
215                 if (e->value == value)
216                         return 0;
217
218                 return -EEXIST;
219         }
220
221         if (!(e = new(struct hashmap_entry, 1)))
222                 return -ENOMEM;
223
224         e->key = key;
225         e->value = value;
226
227         link_entry(h, e, hash);
228
229         return 1;
230 }
231
232 int hashmap_replace(Hashmap *h, const void *key, void *value) {
233         struct hashmap_entry *e;
234         unsigned hash;
235
236         assert(h);
237
238         hash = h->hash_func(key) % NBUCKETS;
239
240         if ((e = hash_scan(h, hash, key))) {
241                 e->key = key;
242                 e->value = value;
243                 return 0;
244         }
245
246         return hashmap_put(h, key, value);
247 }
248
249 void* hashmap_get(Hashmap *h, const void *key) {
250         unsigned hash;
251         struct hashmap_entry *e;
252
253         if (!h)
254                 return NULL;
255
256         hash = h->hash_func(key) % NBUCKETS;
257
258         if (!(e = hash_scan(h, hash, key)))
259                 return NULL;
260
261         return e->value;
262 }
263
264 void* hashmap_remove(Hashmap *h, const void *key) {
265         struct hashmap_entry *e;
266         unsigned hash;
267         void *data;
268
269         if (!h)
270                 return NULL;
271
272         hash = h->hash_func(key) % NBUCKETS;
273
274         if (!(e = hash_scan(h, hash, key)))
275                 return NULL;
276
277         data = e->value;
278         remove_entry(h, e);
279
280         return data;
281 }
282
283 int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
284         struct hashmap_entry *e;
285         unsigned old_hash, new_hash;
286
287         if (!h)
288                 return -ENOENT;
289
290         old_hash = h->hash_func(old_key) % NBUCKETS;
291         if (!(e = hash_scan(h, old_hash, old_key)))
292                 return -ENOENT;
293
294         new_hash = h->hash_func(new_key) % NBUCKETS;
295         if (hash_scan(h, new_hash, new_key))
296                 return -EEXIST;
297
298         unlink_entry(h, e, old_hash);
299
300         e->key = new_key;
301         e->value = value;
302
303         link_entry(h, e, new_hash);
304
305         return 0;
306 }
307
308 int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
309         struct hashmap_entry *e, *k;
310         unsigned old_hash, new_hash;
311
312         if (!h)
313                 return -ENOENT;
314
315         old_hash = h->hash_func(old_key) % NBUCKETS;
316         if (!(e = hash_scan(h, old_hash, old_key)))
317                 return -ENOENT;
318
319         new_hash = h->hash_func(new_key) % NBUCKETS;
320
321         if ((k = hash_scan(h, new_hash, new_key)))
322                 if (e != k)
323                         remove_entry(h, k);
324
325         unlink_entry(h, e, old_hash);
326
327         e->key = new_key;
328         e->value = value;
329
330         link_entry(h, e, new_hash);
331
332         return 0;
333 }
334
335 void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
336         struct hashmap_entry *e;
337         unsigned hash;
338
339         if (!h)
340                 return NULL;
341
342         hash = h->hash_func(key) % NBUCKETS;
343
344         if (!(e = hash_scan(h, hash, key)))
345                 return NULL;
346
347         if (e->value != value)
348                 return NULL;
349
350         remove_entry(h, e);
351
352         return value;
353 }
354
355 void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
356         struct hashmap_entry *e;
357
358         assert(i);
359
360         if (!h)
361                 goto at_end;
362
363         if (*i == ITERATOR_LAST)
364                 goto at_end;
365
366         if (*i == ITERATOR_FIRST && !h->iterate_list_head)
367                 goto at_end;
368
369         e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
370
371         if (e->iterate_next)
372                 *i = (Iterator) e->iterate_next;
373         else
374                 *i = ITERATOR_LAST;
375
376         if (key)
377                 *key = e->key;
378
379         return e->value;
380
381 at_end:
382         *i = ITERATOR_LAST;
383
384         if (key)
385                 *key = NULL;
386
387         return NULL;
388 }
389
390 void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
391         struct hashmap_entry *e;
392
393         assert(i);
394
395         if (!h)
396                 goto at_beginning;
397
398         if (*i == ITERATOR_FIRST)
399                 goto at_beginning;
400
401         if (*i == ITERATOR_LAST && !h->iterate_list_tail)
402                 goto at_beginning;
403
404         e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
405
406         if (e->iterate_previous)
407                 *i = (Iterator) e->iterate_previous;
408         else
409                 *i = ITERATOR_FIRST;
410
411         if (key)
412                 *key = e->key;
413
414         return e->value;
415
416 at_beginning:
417         *i = ITERATOR_FIRST;
418
419         if (key)
420                 *key = NULL;
421
422         return NULL;
423 }
424
425 void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
426         unsigned hash;
427         struct hashmap_entry *e;
428
429         if (!h)
430                 return NULL;
431
432         hash = h->hash_func(key) % NBUCKETS;
433
434         if (!(e = hash_scan(h, hash, key)))
435                 return NULL;
436
437         *i = (Iterator) e;
438
439         return e->value;
440 }
441
442 void* hashmap_first(Hashmap *h) {
443
444         if (!h)
445                 return NULL;
446
447         if (!h->iterate_list_head)
448                 return NULL;
449
450         return h->iterate_list_head->value;
451 }
452
453 void* hashmap_last(Hashmap *h) {
454
455         if (!h)
456                 return NULL;
457
458         if (!h->iterate_list_tail)
459                 return NULL;
460
461         return h->iterate_list_tail->value;
462 }
463
464 void* hashmap_steal_first(Hashmap *h) {
465         void *data;
466
467         if (!h)
468                 return NULL;
469
470         if (!h->iterate_list_head)
471                 return NULL;
472
473         data = h->iterate_list_head->value;
474         remove_entry(h, h->iterate_list_head);
475
476         return data;
477 }
478
479 void* hashmap_steal_first_key(Hashmap *h) {
480         void *key;
481
482         if (!h)
483                 return NULL;
484
485         if (!h->iterate_list_head)
486                 return NULL;
487
488         key = (void*) h->iterate_list_head->key;
489         remove_entry(h, h->iterate_list_head);
490
491         return key;
492 }
493
494 unsigned hashmap_size(Hashmap *h) {
495
496         if (!h)
497                 return 0;
498
499         return h->n_entries;
500 }
501
502 bool hashmap_isempty(Hashmap *h) {
503
504         if (!h)
505                 return true;
506
507         return h->n_entries == 0;
508 }
509
510 int hashmap_merge(Hashmap *h, Hashmap *other) {
511         struct hashmap_entry *e;
512
513         assert(h);
514
515         if (!other)
516                 return 0;
517
518         for (e = other->iterate_list_head; e; e = e->iterate_next) {
519                 int r;
520
521                 if ((r = hashmap_put(h, e->key, e->value)) < 0)
522                         if (r != -EEXIST)
523                                 return r;
524         }
525
526         return 0;
527 }
528
529 void hashmap_move(Hashmap *h, Hashmap *other) {
530         struct hashmap_entry *e, *n;
531
532         assert(h);
533
534         /* The same as hashmap_merge(), but every new item from other
535          * is moved to h. This function is guaranteed to succeed. */
536
537         if (!other)
538                 return;
539
540         for (e = other->iterate_list_head; e; e = n) {
541                 unsigned h_hash, other_hash;
542
543                 n = e->iterate_next;
544
545                 h_hash = h->hash_func(e->key) % NBUCKETS;
546
547                 if (hash_scan(h, h_hash, e->key))
548                         continue;
549
550                 other_hash = other->hash_func(e->key) % NBUCKETS;
551
552                 unlink_entry(other, e, other_hash);
553                 link_entry(h, e, h_hash);
554         }
555 }
556
557 int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
558         unsigned h_hash, other_hash;
559         struct hashmap_entry *e;
560
561         if (!other)
562                 return 0;
563
564         assert(h);
565
566         h_hash = h->hash_func(key) % NBUCKETS;
567         if (hash_scan(h, h_hash, key))
568                 return -EEXIST;
569
570         other_hash = other->hash_func(key) % NBUCKETS;
571         if (!(e = hash_scan(other, other_hash, key)))
572                 return -ENOENT;
573
574         unlink_entry(other, e, other_hash);
575         link_entry(h, e, h_hash);
576
577         return 0;
578 }
579
580 Hashmap *hashmap_copy(Hashmap *h) {
581         Hashmap *copy;
582
583         assert(h);
584
585         if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
586                 return NULL;
587
588         if (hashmap_merge(copy, h) < 0) {
589                 hashmap_free(copy);
590                 return NULL;
591         }
592
593         return copy;
594 }