dbus: minor coding style fixes
[systemd_ALT/systemd_imz.git] / src / shared / hashmap.c
blob0a044b85ad25a7901025d1a8ae2474a83ab00d66
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3 /***
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
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 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
27 #include "util.h"
28 #include "hashmap.h"
29 #include "macro.h"
31 #define NBUCKETS 127
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;
40 struct Hashmap {
41 hash_func_t hash_func;
42 compare_func_t compare_func;
44 struct hashmap_entry *iterate_list_head, *iterate_list_tail;
45 unsigned n_entries;
47 bool from_pool;
50 #define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
52 struct pool {
53 struct pool *next;
54 unsigned n_tiles;
55 unsigned n_used;
58 static struct pool *first_hashmap_pool = NULL;
59 static void *first_hashmap_tile = NULL;
61 static struct pool *first_entry_pool = NULL;
62 static void *first_entry_tile = NULL;
64 static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
65 unsigned i;
67 if (*first_tile) {
68 void *r;
70 r = *first_tile;
71 *first_tile = * (void**) (*first_tile);
72 return r;
75 if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
76 unsigned n;
77 size_t size;
78 struct pool *p;
80 n = *first_pool ? (*first_pool)->n_tiles : 0;
81 n = MAX(512U, n * 2);
82 size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
83 n = (size - ALIGN(sizeof(struct pool))) / tile_size;
85 p = malloc(size);
86 if (!p)
87 return NULL;
89 p->next = *first_pool;
90 p->n_tiles = n;
91 p->n_used = 0;
93 *first_pool = p;
96 i = (*first_pool)->n_used++;
98 return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
101 static void deallocate_tile(void **first_tile, void *p) {
102 * (void**) p = *first_tile;
103 *first_tile = p;
106 #ifndef __OPTIMIZE__
108 static void drop_pool(struct pool *p) {
109 while (p) {
110 struct pool *n;
111 n = p->next;
112 free(p);
113 p = n;
117 __attribute__((destructor)) static void cleanup_pool(void) {
118 /* Be nice to valgrind */
120 drop_pool(first_hashmap_pool);
121 drop_pool(first_entry_pool);
124 #endif
126 unsigned string_hash_func(const void *p) {
127 unsigned hash = 5381;
128 const signed char *c;
130 /* DJB's hash function */
132 for (c = p; *c; c++)
133 hash = (hash << 5) + hash + (unsigned) *c;
135 return hash;
138 int string_compare_func(const void *a, const void *b) {
139 return strcmp(a, b);
142 unsigned trivial_hash_func(const void *p) {
143 return PTR_TO_UINT(p);
146 int trivial_compare_func(const void *a, const void *b) {
147 return a < b ? -1 : (a > b ? 1 : 0);
150 Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
151 bool b;
152 Hashmap *h;
153 size_t size;
155 b = is_main_thread();
157 size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
159 if (b) {
160 h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
161 if (!h)
162 return NULL;
164 memset(h, 0, size);
165 } else {
166 h = malloc0(size);
168 if (!h)
169 return NULL;
172 h->hash_func = hash_func ? hash_func : trivial_hash_func;
173 h->compare_func = compare_func ? compare_func : trivial_compare_func;
175 h->n_entries = 0;
176 h->iterate_list_head = h->iterate_list_tail = NULL;
178 h->from_pool = b;
180 return h;
183 int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
184 assert(h);
186 if (*h)
187 return 0;
189 if (!(*h = hashmap_new(hash_func, compare_func)))
190 return -ENOMEM;
192 return 0;
195 static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
196 assert(h);
197 assert(e);
199 /* Insert into hash table */
200 e->bucket_next = BY_HASH(h)[hash];
201 e->bucket_previous = NULL;
202 if (BY_HASH(h)[hash])
203 BY_HASH(h)[hash]->bucket_previous = e;
204 BY_HASH(h)[hash] = e;
206 /* Insert into iteration list */
207 e->iterate_previous = h->iterate_list_tail;
208 e->iterate_next = NULL;
209 if (h->iterate_list_tail) {
210 assert(h->iterate_list_head);
211 h->iterate_list_tail->iterate_next = e;
212 } else {
213 assert(!h->iterate_list_head);
214 h->iterate_list_head = e;
216 h->iterate_list_tail = e;
218 h->n_entries++;
219 assert(h->n_entries >= 1);
222 static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
223 assert(h);
224 assert(e);
226 /* Remove from iteration list */
227 if (e->iterate_next)
228 e->iterate_next->iterate_previous = e->iterate_previous;
229 else
230 h->iterate_list_tail = e->iterate_previous;
232 if (e->iterate_previous)
233 e->iterate_previous->iterate_next = e->iterate_next;
234 else
235 h->iterate_list_head = e->iterate_next;
237 /* Remove from hash table bucket list */
238 if (e->bucket_next)
239 e->bucket_next->bucket_previous = e->bucket_previous;
241 if (e->bucket_previous)
242 e->bucket_previous->bucket_next = e->bucket_next;
243 else
244 BY_HASH(h)[hash] = e->bucket_next;
246 assert(h->n_entries >= 1);
247 h->n_entries--;
250 static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
251 unsigned hash;
253 assert(h);
254 assert(e);
256 hash = h->hash_func(e->key) % NBUCKETS;
258 unlink_entry(h, e, hash);
260 if (h->from_pool)
261 deallocate_tile(&first_entry_tile, e);
262 else
263 free(e);
266 void hashmap_free(Hashmap*h) {
268 if (!h)
269 return;
271 hashmap_clear(h);
273 if (h->from_pool)
274 deallocate_tile(&first_hashmap_tile, h);
275 else
276 free(h);
279 void hashmap_free_free(Hashmap *h) {
280 if (!h)
281 return;
283 hashmap_clear_free(h);
284 hashmap_free(h);
287 void hashmap_clear(Hashmap *h) {
288 if (!h)
289 return;
291 while (h->iterate_list_head)
292 remove_entry(h, h->iterate_list_head);
295 void hashmap_clear_free(Hashmap *h) {
296 void *p;
298 if (!h)
299 return;
301 while ((p = hashmap_steal_first(h)))
302 free(p);
305 static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
306 struct hashmap_entry *e;
307 assert(h);
308 assert(hash < NBUCKETS);
310 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
311 if (h->compare_func(e->key, key) == 0)
312 return e;
314 return NULL;
317 int hashmap_put(Hashmap *h, const void *key, void *value) {
318 struct hashmap_entry *e;
319 unsigned hash;
321 assert(h);
323 hash = h->hash_func(key) % NBUCKETS;
325 if ((e = hash_scan(h, hash, key))) {
327 if (e->value == value)
328 return 0;
330 return -EEXIST;
333 if (h->from_pool)
334 e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
335 else
336 e = new(struct hashmap_entry, 1);
338 if (!e)
339 return -ENOMEM;
341 e->key = key;
342 e->value = value;
344 link_entry(h, e, hash);
346 return 1;
349 int hashmap_replace(Hashmap *h, const void *key, void *value) {
350 struct hashmap_entry *e;
351 unsigned hash;
353 assert(h);
355 hash = h->hash_func(key) % NBUCKETS;
357 if ((e = hash_scan(h, hash, key))) {
358 e->key = key;
359 e->value = value;
360 return 0;
363 return hashmap_put(h, key, value);
366 void* hashmap_get(Hashmap *h, const void *key) {
367 unsigned hash;
368 struct hashmap_entry *e;
370 if (!h)
371 return NULL;
373 hash = h->hash_func(key) % NBUCKETS;
375 if (!(e = hash_scan(h, hash, key)))
376 return NULL;
378 return e->value;
381 bool hashmap_contains(Hashmap *h, const void *key) {
382 unsigned hash;
384 if (!h)
385 return false;
387 hash = h->hash_func(key) % NBUCKETS;
389 if (!hash_scan(h, hash, key))
390 return false;
392 return true;
395 void* hashmap_remove(Hashmap *h, const void *key) {
396 struct hashmap_entry *e;
397 unsigned hash;
398 void *data;
400 if (!h)
401 return NULL;
403 hash = h->hash_func(key) % NBUCKETS;
405 if (!(e = hash_scan(h, hash, key)))
406 return NULL;
408 data = e->value;
409 remove_entry(h, e);
411 return data;
414 int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
415 struct hashmap_entry *e;
416 unsigned old_hash, new_hash;
418 if (!h)
419 return -ENOENT;
421 old_hash = h->hash_func(old_key) % NBUCKETS;
422 if (!(e = hash_scan(h, old_hash, old_key)))
423 return -ENOENT;
425 new_hash = h->hash_func(new_key) % NBUCKETS;
426 if (hash_scan(h, new_hash, new_key))
427 return -EEXIST;
429 unlink_entry(h, e, old_hash);
431 e->key = new_key;
432 e->value = value;
434 link_entry(h, e, new_hash);
436 return 0;
439 int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
440 struct hashmap_entry *e, *k;
441 unsigned old_hash, new_hash;
443 if (!h)
444 return -ENOENT;
446 old_hash = h->hash_func(old_key) % NBUCKETS;
447 if (!(e = hash_scan(h, old_hash, old_key)))
448 return -ENOENT;
450 new_hash = h->hash_func(new_key) % NBUCKETS;
452 if ((k = hash_scan(h, new_hash, new_key)))
453 if (e != k)
454 remove_entry(h, k);
456 unlink_entry(h, e, old_hash);
458 e->key = new_key;
459 e->value = value;
461 link_entry(h, e, new_hash);
463 return 0;
466 void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
467 struct hashmap_entry *e;
468 unsigned hash;
470 if (!h)
471 return NULL;
473 hash = h->hash_func(key) % NBUCKETS;
475 if (!(e = hash_scan(h, hash, key)))
476 return NULL;
478 if (e->value != value)
479 return NULL;
481 remove_entry(h, e);
483 return value;
486 void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
487 struct hashmap_entry *e;
489 assert(i);
491 if (!h)
492 goto at_end;
494 if (*i == ITERATOR_LAST)
495 goto at_end;
497 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
498 goto at_end;
500 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
502 if (e->iterate_next)
503 *i = (Iterator) e->iterate_next;
504 else
505 *i = ITERATOR_LAST;
507 if (key)
508 *key = e->key;
510 return e->value;
512 at_end:
513 *i = ITERATOR_LAST;
515 if (key)
516 *key = NULL;
518 return NULL;
521 void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
522 struct hashmap_entry *e;
524 assert(i);
526 if (!h)
527 goto at_beginning;
529 if (*i == ITERATOR_FIRST)
530 goto at_beginning;
532 if (*i == ITERATOR_LAST && !h->iterate_list_tail)
533 goto at_beginning;
535 e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
537 if (e->iterate_previous)
538 *i = (Iterator) e->iterate_previous;
539 else
540 *i = ITERATOR_FIRST;
542 if (key)
543 *key = e->key;
545 return e->value;
547 at_beginning:
548 *i = ITERATOR_FIRST;
550 if (key)
551 *key = NULL;
553 return NULL;
556 void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
557 unsigned hash;
558 struct hashmap_entry *e;
560 if (!h)
561 return NULL;
563 hash = h->hash_func(key) % NBUCKETS;
565 if (!(e = hash_scan(h, hash, key)))
566 return NULL;
568 *i = (Iterator) e;
570 return e->value;
573 void* hashmap_first(Hashmap *h) {
575 if (!h)
576 return NULL;
578 if (!h->iterate_list_head)
579 return NULL;
581 return h->iterate_list_head->value;
584 void* hashmap_first_key(Hashmap *h) {
586 if (!h)
587 return NULL;
589 if (!h->iterate_list_head)
590 return NULL;
592 return (void*) h->iterate_list_head->key;
595 void* hashmap_last(Hashmap *h) {
597 if (!h)
598 return NULL;
600 if (!h->iterate_list_tail)
601 return NULL;
603 return h->iterate_list_tail->value;
606 void* hashmap_steal_first(Hashmap *h) {
607 void *data;
609 if (!h)
610 return NULL;
612 if (!h->iterate_list_head)
613 return NULL;
615 data = h->iterate_list_head->value;
616 remove_entry(h, h->iterate_list_head);
618 return data;
621 void* hashmap_steal_first_key(Hashmap *h) {
622 void *key;
624 if (!h)
625 return NULL;
627 if (!h->iterate_list_head)
628 return NULL;
630 key = (void*) h->iterate_list_head->key;
631 remove_entry(h, h->iterate_list_head);
633 return key;
636 unsigned hashmap_size(Hashmap *h) {
638 if (!h)
639 return 0;
641 return h->n_entries;
644 bool hashmap_isempty(Hashmap *h) {
646 if (!h)
647 return true;
649 return h->n_entries == 0;
652 int hashmap_merge(Hashmap *h, Hashmap *other) {
653 struct hashmap_entry *e;
655 assert(h);
657 if (!other)
658 return 0;
660 for (e = other->iterate_list_head; e; e = e->iterate_next) {
661 int r;
663 if ((r = hashmap_put(h, e->key, e->value)) < 0)
664 if (r != -EEXIST)
665 return r;
668 return 0;
671 void hashmap_move(Hashmap *h, Hashmap *other) {
672 struct hashmap_entry *e, *n;
674 assert(h);
676 /* The same as hashmap_merge(), but every new item from other
677 * is moved to h. This function is guaranteed to succeed. */
679 if (!other)
680 return;
682 for (e = other->iterate_list_head; e; e = n) {
683 unsigned h_hash, other_hash;
685 n = e->iterate_next;
687 h_hash = h->hash_func(e->key) % NBUCKETS;
689 if (hash_scan(h, h_hash, e->key))
690 continue;
692 other_hash = other->hash_func(e->key) % NBUCKETS;
694 unlink_entry(other, e, other_hash);
695 link_entry(h, e, h_hash);
699 int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
700 unsigned h_hash, other_hash;
701 struct hashmap_entry *e;
703 if (!other)
704 return 0;
706 assert(h);
708 h_hash = h->hash_func(key) % NBUCKETS;
709 if (hash_scan(h, h_hash, key))
710 return -EEXIST;
712 other_hash = other->hash_func(key) % NBUCKETS;
713 if (!(e = hash_scan(other, other_hash, key)))
714 return -ENOENT;
716 unlink_entry(other, e, other_hash);
717 link_entry(h, e, h_hash);
719 return 0;
722 Hashmap *hashmap_copy(Hashmap *h) {
723 Hashmap *copy;
725 assert(h);
727 if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
728 return NULL;
730 if (hashmap_merge(copy, h) < 0) {
731 hashmap_free(copy);
732 return NULL;
735 return copy;
738 char **hashmap_get_strv(Hashmap *h) {
739 char **sv;
740 Iterator it;
741 char *item;
742 int n;
744 sv = new(char*, h->n_entries+1);
745 if (!sv)
746 return NULL;
748 n = 0;
749 HASHMAP_FOREACH(item, h, it)
750 sv[n++] = item;
751 sv[n] = NULL;
753 return sv;