]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/hashmap.c
26a4eff07fb3b7336d95e585e3e6bd338bdbd4e2
[thirdparty/systemd.git] / src / shared / 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 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.
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 Lesser General Public License for more details.
17
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 ***/
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 bool from_pool;
48 };
49
50 #define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
51
52 struct pool {
53 struct pool *next;
54 unsigned n_tiles;
55 unsigned n_used;
56 };
57
58 static struct pool *first_hashmap_pool = NULL;
59 static void *first_hashmap_tile = NULL;
60
61 static struct pool *first_entry_pool = NULL;
62 static void *first_entry_tile = NULL;
63
64 static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
65 unsigned i;
66
67 if (*first_tile) {
68 void *r;
69
70 r = *first_tile;
71 *first_tile = * (void**) (*first_tile);
72 return r;
73 }
74
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;
79
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;
84
85 p = malloc(size);
86 if (!p)
87 return NULL;
88
89 p->next = *first_pool;
90 p->n_tiles = n;
91 p->n_used = 0;
92
93 *first_pool = p;
94 }
95
96 i = (*first_pool)->n_used++;
97
98 return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
99 }
100
101 static void deallocate_tile(void **first_tile, void *p) {
102 * (void**) p = *first_tile;
103 *first_tile = p;
104 }
105
106 #ifndef __OPTIMIZE__
107
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;
114 }
115 }
116
117 __attribute__((destructor)) static void cleanup_pool(void) {
118 /* Be nice to valgrind */
119
120 drop_pool(first_hashmap_pool);
121 drop_pool(first_entry_pool);
122 }
123
124 #endif
125
126 unsigned string_hash_func(const void *p) {
127 unsigned hash = 5381;
128 const signed char *c;
129
130 /* DJB's hash function */
131
132 for (c = p; *c; c++)
133 hash = (hash << 5) + hash + (unsigned) *c;
134
135 return hash;
136 }
137
138 int string_compare_func(const void *a, const void *b) {
139 return strcmp(a, b);
140 }
141
142 unsigned trivial_hash_func(const void *p) {
143 return PTR_TO_UINT(p);
144 }
145
146 int trivial_compare_func(const void *a, const void *b) {
147 return a < b ? -1 : (a > b ? 1 : 0);
148 }
149
150 Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
151 bool b;
152 Hashmap *h;
153 size_t size;
154
155 b = is_main_thread();
156
157 size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
158
159 if (b) {
160 h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
161 if (!h)
162 return NULL;
163
164 memset(h, 0, size);
165 } else {
166 h = malloc0(size);
167
168 if (!h)
169 return NULL;
170 }
171
172 h->hash_func = hash_func ? hash_func : trivial_hash_func;
173 h->compare_func = compare_func ? compare_func : trivial_compare_func;
174
175 h->n_entries = 0;
176 h->iterate_list_head = h->iterate_list_tail = NULL;
177
178 h->from_pool = b;
179
180 return h;
181 }
182
183 int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
184 assert(h);
185
186 if (*h)
187 return 0;
188
189 if (!(*h = hashmap_new(hash_func, compare_func)))
190 return -ENOMEM;
191
192 return 0;
193 }
194
195 static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
196 assert(h);
197 assert(e);
198
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;
205
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;
215 }
216 h->iterate_list_tail = e;
217
218 h->n_entries++;
219 assert(h->n_entries >= 1);
220 }
221
222 static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
223 assert(h);
224 assert(e);
225
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;
231
232 if (e->iterate_previous)
233 e->iterate_previous->iterate_next = e->iterate_next;
234 else
235 h->iterate_list_head = e->iterate_next;
236
237 /* Remove from hash table bucket list */
238 if (e->bucket_next)
239 e->bucket_next->bucket_previous = e->bucket_previous;
240
241 if (e->bucket_previous)
242 e->bucket_previous->bucket_next = e->bucket_next;
243 else
244 BY_HASH(h)[hash] = e->bucket_next;
245
246 assert(h->n_entries >= 1);
247 h->n_entries--;
248 }
249
250 static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
251 unsigned hash;
252
253 assert(h);
254 assert(e);
255
256 hash = h->hash_func(e->key) % NBUCKETS;
257
258 unlink_entry(h, e, hash);
259
260 if (h->from_pool)
261 deallocate_tile(&first_entry_tile, e);
262 else
263 free(e);
264 }
265
266 void hashmap_free(Hashmap*h) {
267
268 /* Free the hashmap, but nothing in it */
269
270 if (!h)
271 return;
272
273 hashmap_clear(h);
274
275 if (h->from_pool)
276 deallocate_tile(&first_hashmap_tile, h);
277 else
278 free(h);
279 }
280
281 void hashmap_free_free(Hashmap *h) {
282
283 /* Free the hashmap and all data objects in it, but not the
284 * keys */
285
286 if (!h)
287 return;
288
289 hashmap_clear_free(h);
290 hashmap_free(h);
291 }
292
293 void hashmap_clear(Hashmap *h) {
294 if (!h)
295 return;
296
297 while (h->iterate_list_head)
298 remove_entry(h, h->iterate_list_head);
299 }
300
301 void hashmap_clear_free(Hashmap *h) {
302 void *p;
303
304 if (!h)
305 return;
306
307 while ((p = hashmap_steal_first(h)))
308 free(p);
309 }
310
311 static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
312 struct hashmap_entry *e;
313 assert(h);
314 assert(hash < NBUCKETS);
315
316 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
317 if (h->compare_func(e->key, key) == 0)
318 return e;
319
320 return NULL;
321 }
322
323 int hashmap_put(Hashmap *h, const void *key, void *value) {
324 struct hashmap_entry *e;
325 unsigned hash;
326
327 assert(h);
328
329 hash = h->hash_func(key) % NBUCKETS;
330
331 if ((e = hash_scan(h, hash, key))) {
332
333 if (e->value == value)
334 return 0;
335
336 return -EEXIST;
337 }
338
339 if (h->from_pool)
340 e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
341 else
342 e = new(struct hashmap_entry, 1);
343
344 if (!e)
345 return -ENOMEM;
346
347 e->key = key;
348 e->value = value;
349
350 link_entry(h, e, hash);
351
352 return 1;
353 }
354
355 int hashmap_replace(Hashmap *h, const void *key, void *value) {
356 struct hashmap_entry *e;
357 unsigned hash;
358
359 assert(h);
360
361 hash = h->hash_func(key) % NBUCKETS;
362
363 if ((e = hash_scan(h, hash, key))) {
364 e->key = key;
365 e->value = value;
366 return 0;
367 }
368
369 return hashmap_put(h, key, value);
370 }
371
372 void* hashmap_get(Hashmap *h, const void *key) {
373 unsigned hash;
374 struct hashmap_entry *e;
375
376 if (!h)
377 return NULL;
378
379 hash = h->hash_func(key) % NBUCKETS;
380 e = hash_scan(h, hash, key);
381 if (!e)
382 return NULL;
383
384 return e->value;
385 }
386
387 bool hashmap_contains(Hashmap *h, const void *key) {
388 unsigned hash;
389
390 if (!h)
391 return false;
392
393 hash = h->hash_func(key) % NBUCKETS;
394
395 if (!hash_scan(h, hash, key))
396 return false;
397
398 return true;
399 }
400
401 void* hashmap_remove(Hashmap *h, const void *key) {
402 struct hashmap_entry *e;
403 unsigned hash;
404 void *data;
405
406 if (!h)
407 return NULL;
408
409 hash = h->hash_func(key) % NBUCKETS;
410
411 if (!(e = hash_scan(h, hash, key)))
412 return NULL;
413
414 data = e->value;
415 remove_entry(h, e);
416
417 return data;
418 }
419
420 int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
421 struct hashmap_entry *e;
422 unsigned old_hash, new_hash;
423
424 if (!h)
425 return -ENOENT;
426
427 old_hash = h->hash_func(old_key) % NBUCKETS;
428 if (!(e = hash_scan(h, old_hash, old_key)))
429 return -ENOENT;
430
431 new_hash = h->hash_func(new_key) % NBUCKETS;
432 if (hash_scan(h, new_hash, new_key))
433 return -EEXIST;
434
435 unlink_entry(h, e, old_hash);
436
437 e->key = new_key;
438 e->value = value;
439
440 link_entry(h, e, new_hash);
441
442 return 0;
443 }
444
445 int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
446 struct hashmap_entry *e, *k;
447 unsigned old_hash, new_hash;
448
449 if (!h)
450 return -ENOENT;
451
452 old_hash = h->hash_func(old_key) % NBUCKETS;
453 if (!(e = hash_scan(h, old_hash, old_key)))
454 return -ENOENT;
455
456 new_hash = h->hash_func(new_key) % NBUCKETS;
457
458 if ((k = hash_scan(h, new_hash, new_key)))
459 if (e != k)
460 remove_entry(h, k);
461
462 unlink_entry(h, e, old_hash);
463
464 e->key = new_key;
465 e->value = value;
466
467 link_entry(h, e, new_hash);
468
469 return 0;
470 }
471
472 void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
473 struct hashmap_entry *e;
474 unsigned hash;
475
476 if (!h)
477 return NULL;
478
479 hash = h->hash_func(key) % NBUCKETS;
480
481 if (!(e = hash_scan(h, hash, key)))
482 return NULL;
483
484 if (e->value != value)
485 return NULL;
486
487 remove_entry(h, e);
488
489 return value;
490 }
491
492 void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
493 struct hashmap_entry *e;
494
495 assert(i);
496
497 if (!h)
498 goto at_end;
499
500 if (*i == ITERATOR_LAST)
501 goto at_end;
502
503 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
504 goto at_end;
505
506 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
507
508 if (e->iterate_next)
509 *i = (Iterator) e->iterate_next;
510 else
511 *i = ITERATOR_LAST;
512
513 if (key)
514 *key = e->key;
515
516 return e->value;
517
518 at_end:
519 *i = ITERATOR_LAST;
520
521 if (key)
522 *key = NULL;
523
524 return NULL;
525 }
526
527 void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
528 struct hashmap_entry *e;
529
530 assert(i);
531
532 if (!h)
533 goto at_beginning;
534
535 if (*i == ITERATOR_FIRST)
536 goto at_beginning;
537
538 if (*i == ITERATOR_LAST && !h->iterate_list_tail)
539 goto at_beginning;
540
541 e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
542
543 if (e->iterate_previous)
544 *i = (Iterator) e->iterate_previous;
545 else
546 *i = ITERATOR_FIRST;
547
548 if (key)
549 *key = e->key;
550
551 return e->value;
552
553 at_beginning:
554 *i = ITERATOR_FIRST;
555
556 if (key)
557 *key = NULL;
558
559 return NULL;
560 }
561
562 void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
563 unsigned hash;
564 struct hashmap_entry *e;
565
566 if (!h)
567 return NULL;
568
569 hash = h->hash_func(key) % NBUCKETS;
570
571 if (!(e = hash_scan(h, hash, key)))
572 return NULL;
573
574 *i = (Iterator) e;
575
576 return e->value;
577 }
578
579 void* hashmap_first(Hashmap *h) {
580
581 if (!h)
582 return NULL;
583
584 if (!h->iterate_list_head)
585 return NULL;
586
587 return h->iterate_list_head->value;
588 }
589
590 void* hashmap_first_key(Hashmap *h) {
591
592 if (!h)
593 return NULL;
594
595 if (!h->iterate_list_head)
596 return NULL;
597
598 return (void*) h->iterate_list_head->key;
599 }
600
601 void* hashmap_last(Hashmap *h) {
602
603 if (!h)
604 return NULL;
605
606 if (!h->iterate_list_tail)
607 return NULL;
608
609 return h->iterate_list_tail->value;
610 }
611
612 void* hashmap_steal_first(Hashmap *h) {
613 void *data;
614
615 if (!h)
616 return NULL;
617
618 if (!h->iterate_list_head)
619 return NULL;
620
621 data = h->iterate_list_head->value;
622 remove_entry(h, h->iterate_list_head);
623
624 return data;
625 }
626
627 void* hashmap_steal_first_key(Hashmap *h) {
628 void *key;
629
630 if (!h)
631 return NULL;
632
633 if (!h->iterate_list_head)
634 return NULL;
635
636 key = (void*) h->iterate_list_head->key;
637 remove_entry(h, h->iterate_list_head);
638
639 return key;
640 }
641
642 unsigned hashmap_size(Hashmap *h) {
643
644 if (!h)
645 return 0;
646
647 return h->n_entries;
648 }
649
650 bool hashmap_isempty(Hashmap *h) {
651
652 if (!h)
653 return true;
654
655 return h->n_entries == 0;
656 }
657
658 int hashmap_merge(Hashmap *h, Hashmap *other) {
659 struct hashmap_entry *e;
660
661 assert(h);
662
663 if (!other)
664 return 0;
665
666 for (e = other->iterate_list_head; e; e = e->iterate_next) {
667 int r;
668
669 if ((r = hashmap_put(h, e->key, e->value)) < 0)
670 if (r != -EEXIST)
671 return r;
672 }
673
674 return 0;
675 }
676
677 void hashmap_move(Hashmap *h, Hashmap *other) {
678 struct hashmap_entry *e, *n;
679
680 assert(h);
681
682 /* The same as hashmap_merge(), but every new item from other
683 * is moved to h. This function is guaranteed to succeed. */
684
685 if (!other)
686 return;
687
688 for (e = other->iterate_list_head; e; e = n) {
689 unsigned h_hash, other_hash;
690
691 n = e->iterate_next;
692
693 h_hash = h->hash_func(e->key) % NBUCKETS;
694
695 if (hash_scan(h, h_hash, e->key))
696 continue;
697
698 other_hash = other->hash_func(e->key) % NBUCKETS;
699
700 unlink_entry(other, e, other_hash);
701 link_entry(h, e, h_hash);
702 }
703 }
704
705 int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
706 unsigned h_hash, other_hash;
707 struct hashmap_entry *e;
708
709 if (!other)
710 return 0;
711
712 assert(h);
713
714 h_hash = h->hash_func(key) % NBUCKETS;
715 if (hash_scan(h, h_hash, key))
716 return -EEXIST;
717
718 other_hash = other->hash_func(key) % NBUCKETS;
719 if (!(e = hash_scan(other, other_hash, key)))
720 return -ENOENT;
721
722 unlink_entry(other, e, other_hash);
723 link_entry(h, e, h_hash);
724
725 return 0;
726 }
727
728 Hashmap *hashmap_copy(Hashmap *h) {
729 Hashmap *copy;
730
731 assert(h);
732
733 if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
734 return NULL;
735
736 if (hashmap_merge(copy, h) < 0) {
737 hashmap_free(copy);
738 return NULL;
739 }
740
741 return copy;
742 }
743
744 char **hashmap_get_strv(Hashmap *h) {
745 char **sv;
746 Iterator it;
747 char *item;
748 int n;
749
750 sv = new(char*, h->n_entries+1);
751 if (!sv)
752 return NULL;
753
754 n = 0;
755 HASHMAP_FOREACH(item, h, it)
756 sv[n++] = item;
757 sv[n] = NULL;
758
759 return sv;
760 }
761
762 void *hashmap_next(Hashmap *h, const void *key) {
763 unsigned hash;
764 struct hashmap_entry *e;
765
766 assert(h);
767 assert(key);
768
769 if (!h)
770 return NULL;
771
772 hash = h->hash_func(key) % NBUCKETS;
773 e = hash_scan(h, hash, key);
774 if (!e)
775 return NULL;
776
777 e = e->iterate_next;
778 if (!e)
779 return NULL;
780
781 return e->value;
782 }