]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/hashmap.c
journal: include tag object header in hmac
[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) {
61b1477c
LP
280 if (!h)
281 return;
282
9946996c 283 hashmap_clear_free(h);
449ddb2d
LP
284 hashmap_free(h);
285}
286
11dd41ce
LP
287void hashmap_clear(Hashmap *h) {
288 if (!h)
289 return;
290
291 while (h->iterate_list_head)
292 remove_entry(h, h->iterate_list_head);
293}
294
9946996c
LP
295void hashmap_clear_free(Hashmap *h) {
296 void *p;
297
61b1477c
LP
298 if (!h)
299 return;
9946996c
LP
300
301 while ((p = hashmap_steal_first(h)))
302 free(p);
303}
304
60918275
LP
305static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
306 struct hashmap_entry *e;
307 assert(h);
308 assert(hash < NBUCKETS);
309
310 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
311 if (h->compare_func(e->key, key) == 0)
312 return e;
313
314 return NULL;
315}
316
317int hashmap_put(Hashmap *h, const void *key, void *value) {
318 struct hashmap_entry *e;
319 unsigned hash;
320
321 assert(h);
322
323 hash = h->hash_func(key) % NBUCKETS;
324
3158713e
LP
325 if ((e = hash_scan(h, hash, key))) {
326
327 if (e->value == value)
328 return 0;
329
60918275 330 return -EEXIST;
3158713e 331 }
60918275 332
39c2a6f1
LP
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);
337
338 if (!e)
60918275
LP
339 return -ENOMEM;
340
341 e->key = key;
342 e->value = value;
343
101d8e63 344 link_entry(h, e, hash);
60918275 345
48507e66 346 return 1;
60918275
LP
347}
348
3158713e
LP
349int hashmap_replace(Hashmap *h, const void *key, void *value) {
350 struct hashmap_entry *e;
351 unsigned hash;
352
353 assert(h);
354
355 hash = h->hash_func(key) % NBUCKETS;
356
357 if ((e = hash_scan(h, hash, key))) {
101d8e63 358 e->key = key;
3158713e
LP
359 e->value = value;
360 return 0;
361 }
362
363 return hashmap_put(h, key, value);
364}
365
60918275
LP
366void* hashmap_get(Hashmap *h, const void *key) {
367 unsigned hash;
368 struct hashmap_entry *e;
369
370 if (!h)
371 return NULL;
372
373 hash = h->hash_func(key) % NBUCKETS;
374
375 if (!(e = hash_scan(h, hash, key)))
376 return NULL;
377
378 return e->value;
379}
380
381void* hashmap_remove(Hashmap *h, const void *key) {
382 struct hashmap_entry *e;
383 unsigned hash;
384 void *data;
385
386 if (!h)
387 return NULL;
388
389 hash = h->hash_func(key) % NBUCKETS;
390
391 if (!(e = hash_scan(h, hash, key)))
392 return NULL;
393
394 data = e->value;
395 remove_entry(h, e);
396
397 return data;
398}
399
101d8e63
LP
400int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
401 struct hashmap_entry *e;
402 unsigned old_hash, new_hash;
403
404 if (!h)
405 return -ENOENT;
406
407 old_hash = h->hash_func(old_key) % NBUCKETS;
408 if (!(e = hash_scan(h, old_hash, old_key)))
409 return -ENOENT;
410
411 new_hash = h->hash_func(new_key) % NBUCKETS;
412 if (hash_scan(h, new_hash, new_key))
413 return -EEXIST;
414
415 unlink_entry(h, e, old_hash);
416
417 e->key = new_key;
418 e->value = value;
419
420 link_entry(h, e, new_hash);
421
422 return 0;
423}
424
8fe914ec
LP
425int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
426 struct hashmap_entry *e, *k;
427 unsigned old_hash, new_hash;
428
429 if (!h)
430 return -ENOENT;
431
432 old_hash = h->hash_func(old_key) % NBUCKETS;
433 if (!(e = hash_scan(h, old_hash, old_key)))
434 return -ENOENT;
435
436 new_hash = h->hash_func(new_key) % NBUCKETS;
437
438 if ((k = hash_scan(h, new_hash, new_key)))
439 if (e != k)
440 remove_entry(h, k);
441
442 unlink_entry(h, e, old_hash);
443
444 e->key = new_key;
445 e->value = value;
446
447 link_entry(h, e, new_hash);
448
449 return 0;
450}
451
3158713e
LP
452void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
453 struct hashmap_entry *e;
454 unsigned hash;
455
456 if (!h)
457 return NULL;
458
459 hash = h->hash_func(key) % NBUCKETS;
460
461 if (!(e = hash_scan(h, hash, key)))
462 return NULL;
463
464 if (e->value != value)
465 return NULL;
466
467 remove_entry(h, e);
468
469 return value;
470}
471
034c6ed7 472void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
60918275
LP
473 struct hashmap_entry *e;
474
034c6ed7 475 assert(i);
60918275
LP
476
477 if (!h)
478 goto at_end;
479
034c6ed7 480 if (*i == ITERATOR_LAST)
60918275
LP
481 goto at_end;
482
034c6ed7 483 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
60918275
LP
484 goto at_end;
485
034c6ed7 486 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
60918275
LP
487
488 if (e->iterate_next)
034c6ed7 489 *i = (Iterator) e->iterate_next;
60918275 490 else
034c6ed7 491 *i = ITERATOR_LAST;
60918275
LP
492
493 if (key)
494 *key = e->key;
495
496 return e->value;
497
498at_end:
034c6ed7 499 *i = ITERATOR_LAST;
60918275
LP
500
501 if (key)
502 *key = NULL;
503
504 return NULL;
505}
506
034c6ed7 507void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
60918275
LP
508 struct hashmap_entry *e;
509
034c6ed7 510 assert(i);
60918275
LP
511
512 if (!h)
513 goto at_beginning;
514
034c6ed7 515 if (*i == ITERATOR_FIRST)
60918275
LP
516 goto at_beginning;
517
034c6ed7 518 if (*i == ITERATOR_LAST && !h->iterate_list_tail)
60918275
LP
519 goto at_beginning;
520
034c6ed7 521 e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
60918275
LP
522
523 if (e->iterate_previous)
034c6ed7 524 *i = (Iterator) e->iterate_previous;
60918275 525 else
034c6ed7 526 *i = ITERATOR_FIRST;
60918275
LP
527
528 if (key)
529 *key = e->key;
530
531 return e->value;
532
533at_beginning:
034c6ed7 534 *i = ITERATOR_FIRST;
60918275
LP
535
536 if (key)
537 *key = NULL;
538
539 return NULL;
540}
541
034c6ed7
LP
542void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
543 unsigned hash;
544 struct hashmap_entry *e;
545
546 if (!h)
547 return NULL;
548
549 hash = h->hash_func(key) % NBUCKETS;
550
551 if (!(e = hash_scan(h, hash, key)))
552 return NULL;
553
554 *i = (Iterator) e;
555
556 return e->value;
557}
558
60918275
LP
559void* hashmap_first(Hashmap *h) {
560
561 if (!h)
562 return NULL;
563
564 if (!h->iterate_list_head)
565 return NULL;
566
567 return h->iterate_list_head->value;
568}
569
2e4a6ff4
LP
570void* hashmap_first_key(Hashmap *h) {
571
572 if (!h)
573 return NULL;
574
575 if (!h->iterate_list_head)
576 return NULL;
577
578 return (void*) h->iterate_list_head->key;
579}
580
60918275
LP
581void* hashmap_last(Hashmap *h) {
582
583 if (!h)
584 return NULL;
585
586 if (!h->iterate_list_tail)
587 return NULL;
588
589 return h->iterate_list_tail->value;
590}
591
592void* hashmap_steal_first(Hashmap *h) {
593 void *data;
594
595 if (!h)
596 return NULL;
597
598 if (!h->iterate_list_head)
599 return NULL;
600
601 data = h->iterate_list_head->value;
602 remove_entry(h, h->iterate_list_head);
603
604 return data;
605}
606
22be093f
LP
607void* hashmap_steal_first_key(Hashmap *h) {
608 void *key;
609
610 if (!h)
611 return NULL;
612
613 if (!h->iterate_list_head)
614 return NULL;
615
616 key = (void*) h->iterate_list_head->key;
617 remove_entry(h, h->iterate_list_head);
618
619 return key;
620}
621
60918275
LP
622unsigned hashmap_size(Hashmap *h) {
623
624 if (!h)
625 return 0;
626
627 return h->n_entries;
628}
629
630bool hashmap_isempty(Hashmap *h) {
631
632 if (!h)
633 return true;
634
635 return h->n_entries == 0;
636}
91cdde8a
LP
637
638int hashmap_merge(Hashmap *h, Hashmap *other) {
639 struct hashmap_entry *e;
640
641 assert(h);
642
643 if (!other)
644 return 0;
645
646 for (e = other->iterate_list_head; e; e = e->iterate_next) {
647 int r;
648
649 if ((r = hashmap_put(h, e->key, e->value)) < 0)
650 if (r != -EEXIST)
651 return r;
652 }
653
654 return 0;
655}
656
101d8e63
LP
657void hashmap_move(Hashmap *h, Hashmap *other) {
658 struct hashmap_entry *e, *n;
659
660 assert(h);
661
662 /* The same as hashmap_merge(), but every new item from other
663 * is moved to h. This function is guaranteed to succeed. */
664
665 if (!other)
666 return;
667
668 for (e = other->iterate_list_head; e; e = n) {
669 unsigned h_hash, other_hash;
670
671 n = e->iterate_next;
672
673 h_hash = h->hash_func(e->key) % NBUCKETS;
674
675 if (hash_scan(h, h_hash, e->key))
676 continue;
677
678 other_hash = other->hash_func(e->key) % NBUCKETS;
679
680 unlink_entry(other, e, other_hash);
681 link_entry(h, e, h_hash);
682 }
683}
684
685int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
686 unsigned h_hash, other_hash;
687 struct hashmap_entry *e;
688
689 if (!other)
690 return 0;
691
692 assert(h);
693
694 h_hash = h->hash_func(key) % NBUCKETS;
695 if (hash_scan(h, h_hash, key))
696 return -EEXIST;
697
698 other_hash = other->hash_func(key) % NBUCKETS;
699 if (!(e = hash_scan(other, other_hash, key)))
700 return -ENOENT;
701
702 unlink_entry(other, e, other_hash);
703 link_entry(h, e, h_hash);
704
705 return 0;
706}
707
91cdde8a
LP
708Hashmap *hashmap_copy(Hashmap *h) {
709 Hashmap *copy;
710
711 assert(h);
712
713 if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
714 return NULL;
715
716 if (hashmap_merge(copy, h) < 0) {
717 hashmap_free(copy);
718 return NULL;
719 }
720
721 return copy;
722}
db1413d7
KS
723
724char **hashmap_get_strv(Hashmap *h) {
725 char **sv;
726 Iterator it;
729e3769 727 char *item;
db1413d7
KS
728 int n;
729
729e3769
LP
730 sv = new(char*, h->n_entries+1);
731 if (!sv)
db1413d7
KS
732 return NULL;
733
734 n = 0;
729e3769
LP
735 HASHMAP_FOREACH(item, h, it)
736 sv[n++] = item;
db1413d7
KS
737 sv[n] = NULL;
738
739 return sv;
740}