]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/hashmap.c
core: add new "scope" unit type for making a unit of pre-existing processes
[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 #ifdef VALGRIND
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 unsigned uint64_hash_func(const void *p) {
151 uint64_t u;
152
153 assert_cc(sizeof(uint64_t) == 2*sizeof(unsigned));
154
155 u = *(const uint64_t*) p;
156
157 return (unsigned) ((u >> 32) ^ u);
158 }
159
160 int uint64_compare_func(const void *_a, const void *_b) {
161 uint64_t a, b;
162
163 a = *(const uint64_t*) _a;
164 b = *(const uint64_t*) _b;
165
166 return a < b ? -1 : (a > b ? 1 : 0);
167 }
168
169 Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
170 bool b;
171 Hashmap *h;
172 size_t size;
173
174 b = is_main_thread();
175
176 size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
177
178 if (b) {
179 h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
180 if (!h)
181 return NULL;
182
183 memset(h, 0, size);
184 } else {
185 h = malloc0(size);
186
187 if (!h)
188 return NULL;
189 }
190
191 h->hash_func = hash_func ? hash_func : trivial_hash_func;
192 h->compare_func = compare_func ? compare_func : trivial_compare_func;
193
194 h->n_entries = 0;
195 h->iterate_list_head = h->iterate_list_tail = NULL;
196
197 h->from_pool = b;
198
199 return h;
200 }
201
202 int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
203 assert(h);
204
205 if (*h)
206 return 0;
207
208 if (!(*h = hashmap_new(hash_func, compare_func)))
209 return -ENOMEM;
210
211 return 0;
212 }
213
214 static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
215 assert(h);
216 assert(e);
217
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;
224
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;
231 } else {
232 assert(!h->iterate_list_head);
233 h->iterate_list_head = e;
234 }
235 h->iterate_list_tail = e;
236
237 h->n_entries++;
238 assert(h->n_entries >= 1);
239 }
240
241 static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
242 assert(h);
243 assert(e);
244
245 /* Remove from iteration list */
246 if (e->iterate_next)
247 e->iterate_next->iterate_previous = e->iterate_previous;
248 else
249 h->iterate_list_tail = e->iterate_previous;
250
251 if (e->iterate_previous)
252 e->iterate_previous->iterate_next = e->iterate_next;
253 else
254 h->iterate_list_head = e->iterate_next;
255
256 /* Remove from hash table bucket list */
257 if (e->bucket_next)
258 e->bucket_next->bucket_previous = e->bucket_previous;
259
260 if (e->bucket_previous)
261 e->bucket_previous->bucket_next = e->bucket_next;
262 else
263 BY_HASH(h)[hash] = e->bucket_next;
264
265 assert(h->n_entries >= 1);
266 h->n_entries--;
267 }
268
269 static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
270 unsigned hash;
271
272 assert(h);
273 assert(e);
274
275 hash = h->hash_func(e->key) % NBUCKETS;
276
277 unlink_entry(h, e, hash);
278
279 if (h->from_pool)
280 deallocate_tile(&first_entry_tile, e);
281 else
282 free(e);
283 }
284
285 void hashmap_free(Hashmap*h) {
286
287 /* Free the hashmap, but nothing in it */
288
289 if (!h)
290 return;
291
292 hashmap_clear(h);
293
294 if (h->from_pool)
295 deallocate_tile(&first_hashmap_tile, h);
296 else
297 free(h);
298 }
299
300 void hashmap_free_free(Hashmap *h) {
301
302 /* Free the hashmap and all data objects in it, but not the
303 * keys */
304
305 if (!h)
306 return;
307
308 hashmap_clear_free(h);
309 hashmap_free(h);
310 }
311
312 void hashmap_free_free_free(Hashmap *h) {
313
314 /* Free the hashmap and all data and key objects in it */
315
316 if (!h)
317 return;
318
319 hashmap_clear_free_free(h);
320 hashmap_free(h);
321 }
322
323 void hashmap_clear(Hashmap *h) {
324 if (!h)
325 return;
326
327 while (h->iterate_list_head)
328 remove_entry(h, h->iterate_list_head);
329 }
330
331 void hashmap_clear_free(Hashmap *h) {
332 void *p;
333
334 if (!h)
335 return;
336
337 while ((p = hashmap_steal_first(h)))
338 free(p);
339 }
340
341 void hashmap_clear_free_free(Hashmap *h) {
342 if (!h)
343 return;
344
345 while (h->iterate_list_head) {
346 void *a, *b;
347
348 a = h->iterate_list_head->value;
349 b = (void*) h->iterate_list_head->key;
350 remove_entry(h, h->iterate_list_head);
351 free(a);
352 free(b);
353 }
354 }
355
356
357 static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
358 struct hashmap_entry *e;
359 assert(h);
360 assert(hash < NBUCKETS);
361
362 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
363 if (h->compare_func(e->key, key) == 0)
364 return e;
365
366 return NULL;
367 }
368
369 int hashmap_put(Hashmap *h, const void *key, void *value) {
370 struct hashmap_entry *e;
371 unsigned hash;
372
373 assert(h);
374
375 hash = h->hash_func(key) % NBUCKETS;
376
377 e = hash_scan(h, hash, key);
378 if (e) {
379
380 if (e->value == value)
381 return 0;
382
383 return -EEXIST;
384 }
385
386 if (h->from_pool)
387 e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
388 else
389 e = new(struct hashmap_entry, 1);
390
391 if (!e)
392 return -ENOMEM;
393
394 e->key = key;
395 e->value = value;
396
397 link_entry(h, e, hash);
398
399 return 1;
400 }
401
402 int hashmap_replace(Hashmap *h, const void *key, void *value) {
403 struct hashmap_entry *e;
404 unsigned hash;
405
406 assert(h);
407
408 hash = h->hash_func(key) % NBUCKETS;
409 e = hash_scan(h, hash, key);
410 if (e) {
411 e->key = key;
412 e->value = value;
413 return 0;
414 }
415
416 return hashmap_put(h, key, value);
417 }
418
419 int hashmap_update(Hashmap *h, const void *key, void *value) {
420 struct hashmap_entry *e;
421 unsigned hash;
422
423 assert(h);
424
425 hash = h->hash_func(key) % NBUCKETS;
426 e = hash_scan(h, hash, key);
427 if (!e)
428 return -ENOENT;
429
430 e->value = value;
431 return 0;
432 }
433
434 void* hashmap_get(Hashmap *h, const void *key) {
435 unsigned hash;
436 struct hashmap_entry *e;
437
438 if (!h)
439 return NULL;
440
441 hash = h->hash_func(key) % NBUCKETS;
442 e = hash_scan(h, hash, key);
443 if (!e)
444 return NULL;
445
446 return e->value;
447 }
448
449 void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
450 unsigned hash;
451 struct hashmap_entry *e;
452
453 if (!h)
454 return NULL;
455
456 hash = h->hash_func(key) % NBUCKETS;
457 e = hash_scan(h, hash, key);
458 if (!e)
459 return NULL;
460
461 if (key2)
462 *key2 = (void*) e->key;
463
464 return e->value;
465 }
466
467 bool hashmap_contains(Hashmap *h, const void *key) {
468 unsigned hash;
469
470 if (!h)
471 return false;
472
473 hash = h->hash_func(key) % NBUCKETS;
474
475 if (!hash_scan(h, hash, key))
476 return false;
477
478 return true;
479 }
480
481 void* hashmap_remove(Hashmap *h, const void *key) {
482 struct hashmap_entry *e;
483 unsigned hash;
484 void *data;
485
486 if (!h)
487 return NULL;
488
489 hash = h->hash_func(key) % NBUCKETS;
490
491 if (!(e = hash_scan(h, hash, key)))
492 return NULL;
493
494 data = e->value;
495 remove_entry(h, e);
496
497 return data;
498 }
499
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;
503
504 if (!h)
505 return -ENOENT;
506
507 old_hash = h->hash_func(old_key) % NBUCKETS;
508 if (!(e = hash_scan(h, old_hash, old_key)))
509 return -ENOENT;
510
511 new_hash = h->hash_func(new_key) % NBUCKETS;
512 if (hash_scan(h, new_hash, new_key))
513 return -EEXIST;
514
515 unlink_entry(h, e, old_hash);
516
517 e->key = new_key;
518 e->value = value;
519
520 link_entry(h, e, new_hash);
521
522 return 0;
523 }
524
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;
528
529 if (!h)
530 return -ENOENT;
531
532 old_hash = h->hash_func(old_key) % NBUCKETS;
533 if (!(e = hash_scan(h, old_hash, old_key)))
534 return -ENOENT;
535
536 new_hash = h->hash_func(new_key) % NBUCKETS;
537
538 if ((k = hash_scan(h, new_hash, new_key)))
539 if (e != k)
540 remove_entry(h, k);
541
542 unlink_entry(h, e, old_hash);
543
544 e->key = new_key;
545 e->value = value;
546
547 link_entry(h, e, new_hash);
548
549 return 0;
550 }
551
552 void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
553 struct hashmap_entry *e;
554 unsigned hash;
555
556 if (!h)
557 return NULL;
558
559 hash = h->hash_func(key) % NBUCKETS;
560
561 if (!(e = hash_scan(h, hash, key)))
562 return NULL;
563
564 if (e->value != value)
565 return NULL;
566
567 remove_entry(h, e);
568
569 return value;
570 }
571
572 void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
573 struct hashmap_entry *e;
574
575 assert(i);
576
577 if (!h)
578 goto at_end;
579
580 if (*i == ITERATOR_LAST)
581 goto at_end;
582
583 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
584 goto at_end;
585
586 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
587
588 if (e->iterate_next)
589 *i = (Iterator) e->iterate_next;
590 else
591 *i = ITERATOR_LAST;
592
593 if (key)
594 *key = e->key;
595
596 return e->value;
597
598 at_end:
599 *i = ITERATOR_LAST;
600
601 if (key)
602 *key = NULL;
603
604 return NULL;
605 }
606
607 void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
608 struct hashmap_entry *e;
609
610 assert(i);
611
612 if (!h)
613 goto at_beginning;
614
615 if (*i == ITERATOR_FIRST)
616 goto at_beginning;
617
618 if (*i == ITERATOR_LAST && !h->iterate_list_tail)
619 goto at_beginning;
620
621 e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
622
623 if (e->iterate_previous)
624 *i = (Iterator) e->iterate_previous;
625 else
626 *i = ITERATOR_FIRST;
627
628 if (key)
629 *key = e->key;
630
631 return e->value;
632
633 at_beginning:
634 *i = ITERATOR_FIRST;
635
636 if (key)
637 *key = NULL;
638
639 return NULL;
640 }
641
642 void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
643 unsigned hash;
644 struct hashmap_entry *e;
645
646 if (!h)
647 return NULL;
648
649 hash = h->hash_func(key) % NBUCKETS;
650
651 if (!(e = hash_scan(h, hash, key)))
652 return NULL;
653
654 *i = (Iterator) e;
655
656 return e->value;
657 }
658
659 void* hashmap_first(Hashmap *h) {
660
661 if (!h)
662 return NULL;
663
664 if (!h->iterate_list_head)
665 return NULL;
666
667 return h->iterate_list_head->value;
668 }
669
670 void* hashmap_first_key(Hashmap *h) {
671
672 if (!h)
673 return NULL;
674
675 if (!h->iterate_list_head)
676 return NULL;
677
678 return (void*) h->iterate_list_head->key;
679 }
680
681 void* hashmap_last(Hashmap *h) {
682
683 if (!h)
684 return NULL;
685
686 if (!h->iterate_list_tail)
687 return NULL;
688
689 return h->iterate_list_tail->value;
690 }
691
692 void* hashmap_steal_first(Hashmap *h) {
693 void *data;
694
695 if (!h)
696 return NULL;
697
698 if (!h->iterate_list_head)
699 return NULL;
700
701 data = h->iterate_list_head->value;
702 remove_entry(h, h->iterate_list_head);
703
704 return data;
705 }
706
707 void* hashmap_steal_first_key(Hashmap *h) {
708 void *key;
709
710 if (!h)
711 return NULL;
712
713 if (!h->iterate_list_head)
714 return NULL;
715
716 key = (void*) h->iterate_list_head->key;
717 remove_entry(h, h->iterate_list_head);
718
719 return key;
720 }
721
722 unsigned hashmap_size(Hashmap *h) {
723
724 if (!h)
725 return 0;
726
727 return h->n_entries;
728 }
729
730 bool hashmap_isempty(Hashmap *h) {
731
732 if (!h)
733 return true;
734
735 return h->n_entries == 0;
736 }
737
738 int hashmap_merge(Hashmap *h, Hashmap *other) {
739 struct hashmap_entry *e;
740
741 assert(h);
742
743 if (!other)
744 return 0;
745
746 for (e = other->iterate_list_head; e; e = e->iterate_next) {
747 int r;
748
749 if ((r = hashmap_put(h, e->key, e->value)) < 0)
750 if (r != -EEXIST)
751 return r;
752 }
753
754 return 0;
755 }
756
757 void hashmap_move(Hashmap *h, Hashmap *other) {
758 struct hashmap_entry *e, *n;
759
760 assert(h);
761
762 /* The same as hashmap_merge(), but every new item from other
763 * is moved to h. This function is guaranteed to succeed. */
764
765 if (!other)
766 return;
767
768 for (e = other->iterate_list_head; e; e = n) {
769 unsigned h_hash, other_hash;
770
771 n = e->iterate_next;
772
773 h_hash = h->hash_func(e->key) % NBUCKETS;
774
775 if (hash_scan(h, h_hash, e->key))
776 continue;
777
778 other_hash = other->hash_func(e->key) % NBUCKETS;
779
780 unlink_entry(other, e, other_hash);
781 link_entry(h, e, h_hash);
782 }
783 }
784
785 int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
786 unsigned h_hash, other_hash;
787 struct hashmap_entry *e;
788
789 if (!other)
790 return 0;
791
792 assert(h);
793
794 h_hash = h->hash_func(key) % NBUCKETS;
795 if (hash_scan(h, h_hash, key))
796 return -EEXIST;
797
798 other_hash = other->hash_func(key) % NBUCKETS;
799 if (!(e = hash_scan(other, other_hash, key)))
800 return -ENOENT;
801
802 unlink_entry(other, e, other_hash);
803 link_entry(h, e, h_hash);
804
805 return 0;
806 }
807
808 Hashmap *hashmap_copy(Hashmap *h) {
809 Hashmap *copy;
810
811 assert(h);
812
813 if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
814 return NULL;
815
816 if (hashmap_merge(copy, h) < 0) {
817 hashmap_free(copy);
818 return NULL;
819 }
820
821 return copy;
822 }
823
824 char **hashmap_get_strv(Hashmap *h) {
825 char **sv;
826 Iterator it;
827 char *item;
828 int n;
829
830 sv = new(char*, h->n_entries+1);
831 if (!sv)
832 return NULL;
833
834 n = 0;
835 HASHMAP_FOREACH(item, h, it)
836 sv[n++] = item;
837 sv[n] = NULL;
838
839 return sv;
840 }
841
842 void *hashmap_next(Hashmap *h, const void *key) {
843 unsigned hash;
844 struct hashmap_entry *e;
845
846 assert(h);
847 assert(key);
848
849 if (!h)
850 return NULL;
851
852 hash = h->hash_func(key) % NBUCKETS;
853 e = hash_scan(h, hash, key);
854 if (!e)
855 return NULL;
856
857 e = e->iterate_next;
858 if (!e)
859 return NULL;
860
861 return e->value;
862 }