1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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/>.
33 struct hashmap_entry {
36 struct hashmap_entry *bucket_next, *bucket_previous;
37 struct hashmap_entry *iterate_next, *iterate_previous;
41 hash_func_t hash_func;
42 compare_func_t compare_func;
44 struct hashmap_entry *iterate_list_head, *iterate_list_tail;
50 #define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
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) {
71 *first_tile = * (void**) (*first_tile);
75 if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
80 n = *first_pool ? (*first_pool)->n_tiles : 0;
82 size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
83 n = (size - ALIGN(sizeof(struct pool))) / tile_size;
89 p->next = *first_pool;
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;
108 static void drop_pool(struct pool *p) {
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);
126 unsigned string_hash_func(const void *p) {
127 unsigned hash = 5381;
128 const signed char *c;
130 /* DJB's hash function */
133 hash = (hash << 5) + hash + (unsigned) *c;
138 int string_compare_func(const void *a, const void *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 unsigned uint64_hash_func(const void *p) {
153 assert_cc(sizeof(uint64_t) == 2*sizeof(unsigned));
155 u = *(const uint64_t*) p;
157 return (unsigned) ((u >> 32) ^ u);
160 int uint64_compare_func(const void *_a, const void *_b) {
163 a = *(const uint64_t*) _a;
164 b = *(const uint64_t*) _b;
166 return a < b ? -1 : (a > b ? 1 : 0);
169 Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
174 b = is_main_thread();
176 size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
179 h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
191 h->hash_func = hash_func ? hash_func : trivial_hash_func;
192 h->compare_func = compare_func ? compare_func : trivial_compare_func;
195 h->iterate_list_head = h->iterate_list_tail = NULL;
202 int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
208 if (!(*h = hashmap_new(hash_func, compare_func)))
214 static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
218 /* Insert into hash table */
219 e->bucket_next = BY_HASH(h)[hash];
220 e->bucket_previous = NULL;
221 if (BY_HASH(h)[hash])
222 BY_HASH(h)[hash]->bucket_previous = e;
223 BY_HASH(h)[hash] = e;
225 /* Insert into iteration list */
226 e->iterate_previous = h->iterate_list_tail;
227 e->iterate_next = NULL;
228 if (h->iterate_list_tail) {
229 assert(h->iterate_list_head);
230 h->iterate_list_tail->iterate_next = e;
232 assert(!h->iterate_list_head);
233 h->iterate_list_head = e;
235 h->iterate_list_tail = e;
238 assert(h->n_entries >= 1);
241 static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
245 /* Remove from iteration list */
247 e->iterate_next->iterate_previous = e->iterate_previous;
249 h->iterate_list_tail = e->iterate_previous;
251 if (e->iterate_previous)
252 e->iterate_previous->iterate_next = e->iterate_next;
254 h->iterate_list_head = e->iterate_next;
256 /* Remove from hash table bucket list */
258 e->bucket_next->bucket_previous = e->bucket_previous;
260 if (e->bucket_previous)
261 e->bucket_previous->bucket_next = e->bucket_next;
263 BY_HASH(h)[hash] = e->bucket_next;
265 assert(h->n_entries >= 1);
269 static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
275 hash = h->hash_func(e->key) % NBUCKETS;
277 unlink_entry(h, e, hash);
280 deallocate_tile(&first_entry_tile, e);
285 void hashmap_free(Hashmap*h) {
287 /* Free the hashmap, but nothing in it */
295 deallocate_tile(&first_hashmap_tile, h);
300 void hashmap_free_free(Hashmap *h) {
302 /* Free the hashmap and all data objects in it, but not the
308 hashmap_clear_free(h);
312 void hashmap_free_free_free(Hashmap *h) {
314 /* Free the hashmap and all data and key objects in it */
319 hashmap_clear_free_free(h);
323 void hashmap_clear(Hashmap *h) {
327 while (h->iterate_list_head)
328 remove_entry(h, h->iterate_list_head);
331 void hashmap_clear_free(Hashmap *h) {
337 while ((p = hashmap_steal_first(h)))
341 void hashmap_clear_free_free(Hashmap *h) {
345 while (h->iterate_list_head) {
348 a = h->iterate_list_head->value;
349 b = (void*) h->iterate_list_head->key;
350 remove_entry(h, h->iterate_list_head);
357 static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
358 struct hashmap_entry *e;
360 assert(hash < NBUCKETS);
362 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
363 if (h->compare_func(e->key, key) == 0)
369 int hashmap_put(Hashmap *h, const void *key, void *value) {
370 struct hashmap_entry *e;
375 hash = h->hash_func(key) % NBUCKETS;
377 e = hash_scan(h, hash, key);
380 if (e->value == value)
387 e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
389 e = new(struct hashmap_entry, 1);
397 link_entry(h, e, hash);
402 int hashmap_replace(Hashmap *h, const void *key, void *value) {
403 struct hashmap_entry *e;
408 hash = h->hash_func(key) % NBUCKETS;
409 e = hash_scan(h, hash, key);
416 return hashmap_put(h, key, value);
419 int hashmap_update(Hashmap *h, const void *key, void *value) {
420 struct hashmap_entry *e;
425 hash = h->hash_func(key) % NBUCKETS;
426 e = hash_scan(h, hash, key);
434 void* hashmap_get(Hashmap *h, const void *key) {
436 struct hashmap_entry *e;
441 hash = h->hash_func(key) % NBUCKETS;
442 e = hash_scan(h, hash, key);
449 void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
451 struct hashmap_entry *e;
456 hash = h->hash_func(key) % NBUCKETS;
457 e = hash_scan(h, hash, key);
462 *key2 = (void*) e->key;
467 bool hashmap_contains(Hashmap *h, const void *key) {
473 hash = h->hash_func(key) % NBUCKETS;
475 if (!hash_scan(h, hash, key))
481 void* hashmap_remove(Hashmap *h, const void *key) {
482 struct hashmap_entry *e;
489 hash = h->hash_func(key) % NBUCKETS;
491 if (!(e = hash_scan(h, hash, key)))
500 int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
501 struct hashmap_entry *e;
502 unsigned old_hash, new_hash;
507 old_hash = h->hash_func(old_key) % NBUCKETS;
508 if (!(e = hash_scan(h, old_hash, old_key)))
511 new_hash = h->hash_func(new_key) % NBUCKETS;
512 if (hash_scan(h, new_hash, new_key))
515 unlink_entry(h, e, old_hash);
520 link_entry(h, e, new_hash);
525 int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
526 struct hashmap_entry *e, *k;
527 unsigned old_hash, new_hash;
532 old_hash = h->hash_func(old_key) % NBUCKETS;
533 if (!(e = hash_scan(h, old_hash, old_key)))
536 new_hash = h->hash_func(new_key) % NBUCKETS;
538 if ((k = hash_scan(h, new_hash, new_key)))
542 unlink_entry(h, e, old_hash);
547 link_entry(h, e, new_hash);
552 void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
553 struct hashmap_entry *e;
559 hash = h->hash_func(key) % NBUCKETS;
561 if (!(e = hash_scan(h, hash, key)))
564 if (e->value != value)
572 void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
573 struct hashmap_entry *e;
580 if (*i == ITERATOR_LAST)
583 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
586 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
589 *i = (Iterator) e->iterate_next;
607 void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
608 struct hashmap_entry *e;
615 if (*i == ITERATOR_FIRST)
618 if (*i == ITERATOR_LAST && !h->iterate_list_tail)
621 e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
623 if (e->iterate_previous)
624 *i = (Iterator) e->iterate_previous;
642 void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
644 struct hashmap_entry *e;
649 hash = h->hash_func(key) % NBUCKETS;
651 if (!(e = hash_scan(h, hash, key)))
659 void* hashmap_first(Hashmap *h) {
664 if (!h->iterate_list_head)
667 return h->iterate_list_head->value;
670 void* hashmap_first_key(Hashmap *h) {
675 if (!h->iterate_list_head)
678 return (void*) h->iterate_list_head->key;
681 void* hashmap_last(Hashmap *h) {
686 if (!h->iterate_list_tail)
689 return h->iterate_list_tail->value;
692 void* hashmap_steal_first(Hashmap *h) {
698 if (!h->iterate_list_head)
701 data = h->iterate_list_head->value;
702 remove_entry(h, h->iterate_list_head);
707 void* hashmap_steal_first_key(Hashmap *h) {
713 if (!h->iterate_list_head)
716 key = (void*) h->iterate_list_head->key;
717 remove_entry(h, h->iterate_list_head);
722 unsigned hashmap_size(Hashmap *h) {
730 bool hashmap_isempty(Hashmap *h) {
735 return h->n_entries == 0;
738 int hashmap_merge(Hashmap *h, Hashmap *other) {
739 struct hashmap_entry *e;
746 for (e = other->iterate_list_head; e; e = e->iterate_next) {
749 if ((r = hashmap_put(h, e->key, e->value)) < 0)
757 void hashmap_move(Hashmap *h, Hashmap *other) {
758 struct hashmap_entry *e, *n;
762 /* The same as hashmap_merge(), but every new item from other
763 * is moved to h. This function is guaranteed to succeed. */
768 for (e = other->iterate_list_head; e; e = n) {
769 unsigned h_hash, other_hash;
773 h_hash = h->hash_func(e->key) % NBUCKETS;
775 if (hash_scan(h, h_hash, e->key))
778 other_hash = other->hash_func(e->key) % NBUCKETS;
780 unlink_entry(other, e, other_hash);
781 link_entry(h, e, h_hash);
785 int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
786 unsigned h_hash, other_hash;
787 struct hashmap_entry *e;
794 h_hash = h->hash_func(key) % NBUCKETS;
795 if (hash_scan(h, h_hash, key))
798 other_hash = other->hash_func(key) % NBUCKETS;
799 if (!(e = hash_scan(other, other_hash, key)))
802 unlink_entry(other, e, other_hash);
803 link_entry(h, e, h_hash);
808 Hashmap *hashmap_copy(Hashmap *h) {
813 if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
816 if (hashmap_merge(copy, h) < 0) {
824 char **hashmap_get_strv(Hashmap *h) {
830 sv = new(char*, h->n_entries+1);
835 HASHMAP_FOREACH(item, h, it)
842 void *hashmap_next(Hashmap *h, const void *key) {
844 struct hashmap_entry *e;
852 hash = h->hash_func(key) % NBUCKETS;
853 e = hash_scan(h, hash, key);