]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/hashmap.c
load-fragment: a few modernizations
[thirdparty/systemd.git] / src / shared / hashmap.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
60918275 2
a7334b09
LP
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
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
60918275
LP
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
33struct hashmap_entry {
34 const void *key;
35 void *value;
60918275
LP
36 struct hashmap_entry *bucket_next, *bucket_previous;
37 struct hashmap_entry *iterate_next, *iterate_previous;
38};
39
40struct 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;
39c2a6f1
LP
46
47 bool from_pool;
60918275
LP
48};
49
50#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
51
39c2a6f1
LP
52struct pool {
53 struct pool *next;
54 unsigned n_tiles;
55 unsigned n_used;
56};
57
6a39419f 58static struct pool *first_hashmap_pool = NULL;
39c2a6f1
LP
59static void *first_hashmap_tile = NULL;
60
6a39419f 61static struct pool *first_entry_pool = NULL;
39c2a6f1
LP
62static void *first_entry_tile = NULL;
63
64static 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
101static void deallocate_tile(void **first_tile, void *p) {
102 * (void**) p = *first_tile;
103 *first_tile = p;
104}
105
106#ifndef __OPTIMIZE__
107
108static 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
60918275 126unsigned string_hash_func(const void *p) {
7dfe96ee
LP
127 unsigned hash = 5381;
128 const signed char *c;
129
130 /* DJB's hash function */
60918275
LP
131
132 for (c = p; *c; c++)
7dfe96ee 133 hash = (hash << 5) + hash + (unsigned) *c;
60918275
LP
134
135 return hash;
136}
137
138int string_compare_func(const void *a, const void *b) {
139 return strcmp(a, b);
140}
141
142unsigned trivial_hash_func(const void *p) {
143 return PTR_TO_UINT(p);
144}
145
146int trivial_compare_func(const void *a, const void *b) {
147 return a < b ? -1 : (a > b ? 1 : 0);
148}
149
150Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
39c2a6f1 151 bool b;
60918275 152 Hashmap *h;
39c2a6f1
LP
153 size_t size;
154
155 b = is_main_thread();
156
157 size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
60918275 158
39c2a6f1
LP
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 }
60918275
LP
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
39c2a6f1
LP
178 h->from_pool = b;
179
60918275
LP
180 return h;
181}
182
034c6ed7
LP
183int 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
101d8e63
LP
195static 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
222static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
60918275
LP
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;
101d8e63 243 else
60918275 244 BY_HASH(h)[hash] = e->bucket_next;
60918275
LP
245
246 assert(h->n_entries >= 1);
247 h->n_entries--;
248}
249
101d8e63
LP
250static 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);
39c2a6f1
LP
259
260 if (h->from_pool)
261 deallocate_tile(&first_entry_tile, e);
262 else
263 free(e);
101d8e63
LP
264}
265
60918275
LP
266void hashmap_free(Hashmap*h) {
267
268 if (!h)
269 return;
270
11dd41ce 271 hashmap_clear(h);
60918275 272
39c2a6f1
LP
273 if (h->from_pool)
274 deallocate_tile(&first_hashmap_tile, h);
275 else
276 free(h);
60918275
LP
277}
278
449ddb2d 279void hashmap_free_free(Hashmap *h) {
9946996c 280 hashmap_clear_free(h);
449ddb2d
LP
281 hashmap_free(h);
282}
283
11dd41ce
LP
284void hashmap_clear(Hashmap *h) {
285 if (!h)
286 return;
287
288 while (h->iterate_list_head)
289 remove_entry(h, h->iterate_list_head);
290}
291
9946996c
LP
292void hashmap_clear_free(Hashmap *h) {
293 void *p;
294
295 assert(h);
296
297 while ((p = hashmap_steal_first(h)))
298 free(p);
299}
300
60918275
LP
301static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
302 struct hashmap_entry *e;
303 assert(h);
304 assert(hash < NBUCKETS);
305
306 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
307 if (h->compare_func(e->key, key) == 0)
308 return e;
309
310 return NULL;
311}
312
313int hashmap_put(Hashmap *h, const void *key, void *value) {
314 struct hashmap_entry *e;
315 unsigned hash;
316
317 assert(h);
318
319 hash = h->hash_func(key) % NBUCKETS;
320
3158713e
LP
321 if ((e = hash_scan(h, hash, key))) {
322
323 if (e->value == value)
324 return 0;
325
60918275 326 return -EEXIST;
3158713e 327 }
60918275 328
39c2a6f1
LP
329 if (h->from_pool)
330 e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
331 else
332 e = new(struct hashmap_entry, 1);
333
334 if (!e)
60918275
LP
335 return -ENOMEM;
336
337 e->key = key;
338 e->value = value;
339
101d8e63 340 link_entry(h, e, hash);
60918275 341
48507e66 342 return 1;
60918275
LP
343}
344
3158713e
LP
345int hashmap_replace(Hashmap *h, const void *key, void *value) {
346 struct hashmap_entry *e;
347 unsigned hash;
348
349 assert(h);
350
351 hash = h->hash_func(key) % NBUCKETS;
352
353 if ((e = hash_scan(h, hash, key))) {
101d8e63 354 e->key = key;
3158713e
LP
355 e->value = value;
356 return 0;
357 }
358
359 return hashmap_put(h, key, value);
360}
361
60918275
LP
362void* hashmap_get(Hashmap *h, const void *key) {
363 unsigned hash;
364 struct hashmap_entry *e;
365
366 if (!h)
367 return NULL;
368
369 hash = h->hash_func(key) % NBUCKETS;
370
371 if (!(e = hash_scan(h, hash, key)))
372 return NULL;
373
374 return e->value;
375}
376
377void* hashmap_remove(Hashmap *h, const void *key) {
378 struct hashmap_entry *e;
379 unsigned hash;
380 void *data;
381
382 if (!h)
383 return NULL;
384
385 hash = h->hash_func(key) % NBUCKETS;
386
387 if (!(e = hash_scan(h, hash, key)))
388 return NULL;
389
390 data = e->value;
391 remove_entry(h, e);
392
393 return data;
394}
395
101d8e63
LP
396int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
397 struct hashmap_entry *e;
398 unsigned old_hash, new_hash;
399
400 if (!h)
401 return -ENOENT;
402
403 old_hash = h->hash_func(old_key) % NBUCKETS;
404 if (!(e = hash_scan(h, old_hash, old_key)))
405 return -ENOENT;
406
407 new_hash = h->hash_func(new_key) % NBUCKETS;
408 if (hash_scan(h, new_hash, new_key))
409 return -EEXIST;
410
411 unlink_entry(h, e, old_hash);
412
413 e->key = new_key;
414 e->value = value;
415
416 link_entry(h, e, new_hash);
417
418 return 0;
419}
420
8fe914ec
LP
421int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
422 struct hashmap_entry *e, *k;
423 unsigned old_hash, new_hash;
424
425 if (!h)
426 return -ENOENT;
427
428 old_hash = h->hash_func(old_key) % NBUCKETS;
429 if (!(e = hash_scan(h, old_hash, old_key)))
430 return -ENOENT;
431
432 new_hash = h->hash_func(new_key) % NBUCKETS;
433
434 if ((k = hash_scan(h, new_hash, new_key)))
435 if (e != k)
436 remove_entry(h, k);
437
438 unlink_entry(h, e, old_hash);
439
440 e->key = new_key;
441 e->value = value;
442
443 link_entry(h, e, new_hash);
444
445 return 0;
446}
447
3158713e
LP
448void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
449 struct hashmap_entry *e;
450 unsigned hash;
451
452 if (!h)
453 return NULL;
454
455 hash = h->hash_func(key) % NBUCKETS;
456
457 if (!(e = hash_scan(h, hash, key)))
458 return NULL;
459
460 if (e->value != value)
461 return NULL;
462
463 remove_entry(h, e);
464
465 return value;
466}
467
034c6ed7 468void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
60918275
LP
469 struct hashmap_entry *e;
470
034c6ed7 471 assert(i);
60918275
LP
472
473 if (!h)
474 goto at_end;
475
034c6ed7 476 if (*i == ITERATOR_LAST)
60918275
LP
477 goto at_end;
478
034c6ed7 479 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
60918275
LP
480 goto at_end;
481
034c6ed7 482 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
60918275
LP
483
484 if (e->iterate_next)
034c6ed7 485 *i = (Iterator) e->iterate_next;
60918275 486 else
034c6ed7 487 *i = ITERATOR_LAST;
60918275
LP
488
489 if (key)
490 *key = e->key;
491
492 return e->value;
493
494at_end:
034c6ed7 495 *i = ITERATOR_LAST;
60918275
LP
496
497 if (key)
498 *key = NULL;
499
500 return NULL;
501}
502
034c6ed7 503void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
60918275
LP
504 struct hashmap_entry *e;
505
034c6ed7 506 assert(i);
60918275
LP
507
508 if (!h)
509 goto at_beginning;
510
034c6ed7 511 if (*i == ITERATOR_FIRST)
60918275
LP
512 goto at_beginning;
513
034c6ed7 514 if (*i == ITERATOR_LAST && !h->iterate_list_tail)
60918275
LP
515 goto at_beginning;
516
034c6ed7 517 e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
60918275
LP
518
519 if (e->iterate_previous)
034c6ed7 520 *i = (Iterator) e->iterate_previous;
60918275 521 else
034c6ed7 522 *i = ITERATOR_FIRST;
60918275
LP
523
524 if (key)
525 *key = e->key;
526
527 return e->value;
528
529at_beginning:
034c6ed7 530 *i = ITERATOR_FIRST;
60918275
LP
531
532 if (key)
533 *key = NULL;
534
535 return NULL;
536}
537
034c6ed7
LP
538void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
539 unsigned hash;
540 struct hashmap_entry *e;
541
542 if (!h)
543 return NULL;
544
545 hash = h->hash_func(key) % NBUCKETS;
546
547 if (!(e = hash_scan(h, hash, key)))
548 return NULL;
549
550 *i = (Iterator) e;
551
552 return e->value;
553}
554
60918275
LP
555void* hashmap_first(Hashmap *h) {
556
557 if (!h)
558 return NULL;
559
560 if (!h->iterate_list_head)
561 return NULL;
562
563 return h->iterate_list_head->value;
564}
565
2e4a6ff4
LP
566void* hashmap_first_key(Hashmap *h) {
567
568 if (!h)
569 return NULL;
570
571 if (!h->iterate_list_head)
572 return NULL;
573
574 return (void*) h->iterate_list_head->key;
575}
576
60918275
LP
577void* hashmap_last(Hashmap *h) {
578
579 if (!h)
580 return NULL;
581
582 if (!h->iterate_list_tail)
583 return NULL;
584
585 return h->iterate_list_tail->value;
586}
587
588void* hashmap_steal_first(Hashmap *h) {
589 void *data;
590
591 if (!h)
592 return NULL;
593
594 if (!h->iterate_list_head)
595 return NULL;
596
597 data = h->iterate_list_head->value;
598 remove_entry(h, h->iterate_list_head);
599
600 return data;
601}
602
22be093f
LP
603void* hashmap_steal_first_key(Hashmap *h) {
604 void *key;
605
606 if (!h)
607 return NULL;
608
609 if (!h->iterate_list_head)
610 return NULL;
611
612 key = (void*) h->iterate_list_head->key;
613 remove_entry(h, h->iterate_list_head);
614
615 return key;
616}
617
60918275
LP
618unsigned hashmap_size(Hashmap *h) {
619
620 if (!h)
621 return 0;
622
623 return h->n_entries;
624}
625
626bool hashmap_isempty(Hashmap *h) {
627
628 if (!h)
629 return true;
630
631 return h->n_entries == 0;
632}
91cdde8a
LP
633
634int hashmap_merge(Hashmap *h, Hashmap *other) {
635 struct hashmap_entry *e;
636
637 assert(h);
638
639 if (!other)
640 return 0;
641
642 for (e = other->iterate_list_head; e; e = e->iterate_next) {
643 int r;
644
645 if ((r = hashmap_put(h, e->key, e->value)) < 0)
646 if (r != -EEXIST)
647 return r;
648 }
649
650 return 0;
651}
652
101d8e63
LP
653void hashmap_move(Hashmap *h, Hashmap *other) {
654 struct hashmap_entry *e, *n;
655
656 assert(h);
657
658 /* The same as hashmap_merge(), but every new item from other
659 * is moved to h. This function is guaranteed to succeed. */
660
661 if (!other)
662 return;
663
664 for (e = other->iterate_list_head; e; e = n) {
665 unsigned h_hash, other_hash;
666
667 n = e->iterate_next;
668
669 h_hash = h->hash_func(e->key) % NBUCKETS;
670
671 if (hash_scan(h, h_hash, e->key))
672 continue;
673
674 other_hash = other->hash_func(e->key) % NBUCKETS;
675
676 unlink_entry(other, e, other_hash);
677 link_entry(h, e, h_hash);
678 }
679}
680
681int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
682 unsigned h_hash, other_hash;
683 struct hashmap_entry *e;
684
685 if (!other)
686 return 0;
687
688 assert(h);
689
690 h_hash = h->hash_func(key) % NBUCKETS;
691 if (hash_scan(h, h_hash, key))
692 return -EEXIST;
693
694 other_hash = other->hash_func(key) % NBUCKETS;
695 if (!(e = hash_scan(other, other_hash, key)))
696 return -ENOENT;
697
698 unlink_entry(other, e, other_hash);
699 link_entry(h, e, h_hash);
700
701 return 0;
702}
703
91cdde8a
LP
704Hashmap *hashmap_copy(Hashmap *h) {
705 Hashmap *copy;
706
707 assert(h);
708
709 if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
710 return NULL;
711
712 if (hashmap_merge(copy, h) < 0) {
713 hashmap_free(copy);
714 return NULL;
715 }
716
717 return copy;
718}
db1413d7
KS
719
720char **hashmap_get_strv(Hashmap *h) {
721 char **sv;
722 Iterator it;
729e3769 723 char *item;
db1413d7
KS
724 int n;
725
729e3769
LP
726 sv = new(char*, h->n_entries+1);
727 if (!sv)
db1413d7
KS
728 return NULL;
729
730 n = 0;
729e3769
LP
731 HASHMAP_FOREACH(item, h, it)
732 sv[n++] = item;
db1413d7
KS
733 sv[n] = NULL;
734
735 return sv;
736}