]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/hashmap.c
hashmap: return more information from resize_buckets()
[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"
9bf3b535 30#include "siphash24.h"
b3dcf58e 31#include "mempool.h"
60918275 32
45fa9e29 33#define INITIAL_N_BUCKETS 31
60918275
LP
34
35struct hashmap_entry {
36 const void *key;
37 void *value;
60918275
LP
38 struct hashmap_entry *bucket_next, *bucket_previous;
39 struct hashmap_entry *iterate_next, *iterate_previous;
40};
41
42struct Hashmap {
d5099efc 43 const struct hash_ops *hash_ops;
60918275
LP
44
45 struct hashmap_entry *iterate_list_head, *iterate_list_tail;
45fa9e29
LP
46
47 struct hashmap_entry ** buckets;
48 unsigned n_buckets, n_entries;
39c2a6f1 49
9bf3b535
LP
50 uint8_t hash_key[HASH_KEY_SIZE];
51 bool from_pool:1;
60918275
LP
52};
53
b3dcf58e
MS
54struct hashmap_tile {
55 Hashmap h;
56 struct hashmap_entry *initial_buckets[INITIAL_N_BUCKETS];
39c2a6f1
LP
57};
58
b3dcf58e
MS
59static DEFINE_MEMPOOL(hashmap_pool, struct hashmap_tile, 8);
60static DEFINE_MEMPOOL(hashmap_entry_pool, struct hashmap_entry, 64);
39c2a6f1 61
090cafa0 62#ifdef VALGRIND
39c2a6f1 63
b3dcf58e 64__attribute__((destructor)) static void cleanup_pools(void) {
39c2a6f1
LP
65 /* Be nice to valgrind */
66
b3dcf58e
MS
67 mempool_drop(&hashmap_entry_pool);
68 mempool_drop(&hashmap_pool);
39c2a6f1
LP
69}
70
71#endif
72
9bf3b535
LP
73unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
74 uint64_t u;
75 siphash24((uint8_t*) &u, p, strlen(p), hash_key);
76 return (unsigned long) u;
60918275
LP
77}
78
79int string_compare_func(const void *a, const void *b) {
80 return strcmp(a, b);
81}
82
d5099efc
MS
83const struct hash_ops string_hash_ops = {
84 .hash = string_hash_func,
85 .compare = string_compare_func
86};
87
9bf3b535
LP
88unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
89 uint64_t u;
90 siphash24((uint8_t*) &u, &p, sizeof(p), hash_key);
91 return (unsigned long) u;
60918275
LP
92}
93
94int trivial_compare_func(const void *a, const void *b) {
95 return a < b ? -1 : (a > b ? 1 : 0);
96}
97
d5099efc
MS
98const struct hash_ops trivial_hash_ops = {
99 .hash = trivial_hash_func,
100 .compare = trivial_compare_func
101};
102
9bf3b535 103unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
a4bcff5b 104 uint64_t u;
9bf3b535
LP
105 siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key);
106 return (unsigned long) u;
a4bcff5b
LP
107}
108
109int uint64_compare_func(const void *_a, const void *_b) {
110 uint64_t a, b;
a4bcff5b
LP
111 a = *(const uint64_t*) _a;
112 b = *(const uint64_t*) _b;
a4bcff5b
LP
113 return a < b ? -1 : (a > b ? 1 : 0);
114}
115
d5099efc
MS
116const struct hash_ops uint64_hash_ops = {
117 .hash = uint64_hash_func,
118 .compare = uint64_compare_func
119};
120
de99c9dc
LP
121#if SIZEOF_DEV_T != 8
122unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
123 uint64_t u;
124 siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key);
125 return (unsigned long) u;
126}
127
128int devt_compare_func(const void *_a, const void *_b) {
129 dev_t a, b;
130 a = *(const dev_t*) _a;
131 b = *(const dev_t*) _b;
132 return a < b ? -1 : (a > b ? 1 : 0);
133}
d5099efc
MS
134
135const struct hash_ops devt_hash_ops = {
136 .hash = devt_hash_func,
137 .compare = devt_compare_func
138};
de99c9dc
LP
139#endif
140
a3b6fafe 141static unsigned bucket_hash(Hashmap *h, const void *p) {
d5099efc 142 return (unsigned) (h->hash_ops->hash(p, h->hash_key) % h->n_buckets);
9bf3b535
LP
143}
144
145static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
146 static uint8_t current[HASH_KEY_SIZE];
147 static bool current_initialized = false;
148
149 /* Returns a hash function key to use. In order to keep things
150 * fast we will not generate a new key each time we allocate a
151 * new hash table. Instead, we'll just reuse the most recently
152 * generated one, except if we never generated one or when we
153 * are rehashing an entire hash table because we reached a
154 * fill level */
155
156 if (!current_initialized || !reuse_is_ok) {
157 random_bytes(current, sizeof(current));
158 current_initialized = true;
159 }
160
161 memcpy(hash_key, current, sizeof(current));
a3b6fafe
LP
162}
163
d5099efc 164Hashmap *hashmap_new(const struct hash_ops *hash_ops) {
39c2a6f1 165 bool b;
b3dcf58e 166 struct hashmap_tile *ht;
60918275 167 Hashmap *h;
39c2a6f1
LP
168
169 b = is_main_thread();
170
39c2a6f1 171 if (b) {
b3dcf58e
MS
172 ht = mempool_alloc_tile(&hashmap_pool);
173 if (!ht)
39c2a6f1
LP
174 return NULL;
175
b3dcf58e 176 memzero(ht, sizeof(struct hashmap_tile));
39c2a6f1 177 } else {
b3dcf58e 178 ht = malloc0(sizeof(struct hashmap_tile));
39c2a6f1 179
b3dcf58e 180 if (!ht)
39c2a6f1
LP
181 return NULL;
182 }
60918275 183
b3dcf58e 184 h = &ht->h;
d5099efc 185 h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
60918275 186
45fa9e29 187 h->n_buckets = INITIAL_N_BUCKETS;
60918275
LP
188 h->n_entries = 0;
189 h->iterate_list_head = h->iterate_list_tail = NULL;
190
b3dcf58e 191 h->buckets = ht->initial_buckets;
45fa9e29 192
39c2a6f1
LP
193 h->from_pool = b;
194
9bf3b535 195 get_hash_key(h->hash_key, true);
a3b6fafe 196
60918275
LP
197 return h;
198}
199
d5099efc 200int hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops) {
45fa9e29
LP
201 Hashmap *q;
202
034c6ed7
LP
203 assert(h);
204
205 if (*h)
206 return 0;
207
d5099efc 208 q = hashmap_new(hash_ops);
45fa9e29 209 if (!q)
034c6ed7
LP
210 return -ENOMEM;
211
45fa9e29 212 *h = q;
034c6ed7
LP
213 return 0;
214}
215
101d8e63
LP
216static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
217 assert(h);
218 assert(e);
219
220 /* Insert into hash table */
45fa9e29 221 e->bucket_next = h->buckets[hash];
101d8e63 222 e->bucket_previous = NULL;
45fa9e29
LP
223 if (h->buckets[hash])
224 h->buckets[hash]->bucket_previous = e;
225 h->buckets[hash] = e;
101d8e63
LP
226
227 /* Insert into iteration list */
228 e->iterate_previous = h->iterate_list_tail;
229 e->iterate_next = NULL;
230 if (h->iterate_list_tail) {
231 assert(h->iterate_list_head);
232 h->iterate_list_tail->iterate_next = e;
233 } else {
234 assert(!h->iterate_list_head);
235 h->iterate_list_head = e;
236 }
237 h->iterate_list_tail = e;
238
239 h->n_entries++;
240 assert(h->n_entries >= 1);
241}
242
243static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
60918275
LP
244 assert(h);
245 assert(e);
246
247 /* Remove from iteration list */
248 if (e->iterate_next)
249 e->iterate_next->iterate_previous = e->iterate_previous;
250 else
251 h->iterate_list_tail = e->iterate_previous;
252
253 if (e->iterate_previous)
254 e->iterate_previous->iterate_next = e->iterate_next;
255 else
256 h->iterate_list_head = e->iterate_next;
257
258 /* Remove from hash table bucket list */
259 if (e->bucket_next)
260 e->bucket_next->bucket_previous = e->bucket_previous;
261
262 if (e->bucket_previous)
263 e->bucket_previous->bucket_next = e->bucket_next;
101d8e63 264 else
45fa9e29 265 h->buckets[hash] = e->bucket_next;
60918275
LP
266
267 assert(h->n_entries >= 1);
268 h->n_entries--;
269}
270
101d8e63
LP
271static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
272 unsigned hash;
273
274 assert(h);
275 assert(e);
276
a3b6fafe 277 hash = bucket_hash(h, e->key);
101d8e63 278 unlink_entry(h, e, hash);
39c2a6f1
LP
279
280 if (h->from_pool)
b3dcf58e 281 mempool_free_tile(&hashmap_entry_pool, e);
39c2a6f1
LP
282 else
283 free(e);
101d8e63
LP
284}
285
60918275
LP
286void hashmap_free(Hashmap*h) {
287
67f3c402
LP
288 /* Free the hashmap, but nothing in it */
289
60918275
LP
290 if (!h)
291 return;
292
11dd41ce 293 hashmap_clear(h);
60918275 294
45fa9e29
LP
295 if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
296 free(h->buckets);
297
39c2a6f1 298 if (h->from_pool)
b3dcf58e 299 mempool_free_tile(&hashmap_pool, container_of(h, struct hashmap_tile, h));
39c2a6f1
LP
300 else
301 free(h);
60918275
LP
302}
303
449ddb2d 304void hashmap_free_free(Hashmap *h) {
67f3c402
LP
305
306 /* Free the hashmap and all data objects in it, but not the
307 * keys */
308
61b1477c
LP
309 if (!h)
310 return;
311
9946996c 312 hashmap_clear_free(h);
449ddb2d
LP
313 hashmap_free(h);
314}
315
fabe5c0e
LP
316void hashmap_free_free_free(Hashmap *h) {
317
318 /* Free the hashmap and all data and key objects in it */
319
320 if (!h)
321 return;
322
323 hashmap_clear_free_free(h);
324 hashmap_free(h);
325}
326
11dd41ce
LP
327void hashmap_clear(Hashmap *h) {
328 if (!h)
329 return;
330
331 while (h->iterate_list_head)
332 remove_entry(h, h->iterate_list_head);
333}
334
9946996c
LP
335void hashmap_clear_free(Hashmap *h) {
336 void *p;
337
61b1477c
LP
338 if (!h)
339 return;
9946996c
LP
340
341 while ((p = hashmap_steal_first(h)))
342 free(p);
343}
344
fabe5c0e
LP
345void hashmap_clear_free_free(Hashmap *h) {
346 if (!h)
347 return;
348
349 while (h->iterate_list_head) {
350 void *a, *b;
351
352 a = h->iterate_list_head->value;
353 b = (void*) h->iterate_list_head->key;
354 remove_entry(h, h->iterate_list_head);
355 free(a);
356 free(b);
357 }
358}
359
60918275
LP
360static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
361 struct hashmap_entry *e;
362 assert(h);
45fa9e29 363 assert(hash < h->n_buckets);
60918275 364
45fa9e29 365 for (e = h->buckets[hash]; e; e = e->bucket_next)
d5099efc 366 if (h->hash_ops->compare(e->key, key) == 0)
60918275
LP
367 return e;
368
369 return NULL;
370}
371
9700d698 372static int resize_buckets(Hashmap *h) {
45fa9e29 373 struct hashmap_entry **n, *i;
9bf3b535
LP
374 unsigned m;
375 uint8_t nkey[HASH_KEY_SIZE];
45fa9e29
LP
376
377 assert(h);
378
379 if (_likely_(h->n_entries*4 < h->n_buckets*3))
9700d698 380 return 0;
45fa9e29
LP
381
382 /* Increase by four */
383 m = (h->n_entries+1)*4-1;
384
385 /* If we hit OOM we simply risk packed hashmaps... */
386 n = new0(struct hashmap_entry*, m);
387 if (!n)
9700d698 388 return -ENOMEM;
45fa9e29 389
9bf3b535 390 /* Let's use a different randomized hash key for the
a3b6fafe
LP
391 * extension, so that people cannot guess what we are using
392 * here forever */
9bf3b535 393 get_hash_key(nkey, false);
a3b6fafe 394
45fa9e29 395 for (i = h->iterate_list_head; i; i = i->iterate_next) {
9bf3b535 396 unsigned long old_bucket, new_bucket;
45fa9e29 397
d5099efc 398 old_bucket = h->hash_ops->hash(i->key, h->hash_key) % h->n_buckets;
45fa9e29
LP
399
400 /* First, drop from old bucket table */
401 if (i->bucket_next)
402 i->bucket_next->bucket_previous = i->bucket_previous;
403
404 if (i->bucket_previous)
405 i->bucket_previous->bucket_next = i->bucket_next;
406 else
9bf3b535 407 h->buckets[old_bucket] = i->bucket_next;
45fa9e29
LP
408
409 /* Then, add to new backet table */
d5099efc 410 new_bucket = h->hash_ops->hash(i->key, nkey) % m;
45fa9e29 411
9bf3b535 412 i->bucket_next = n[new_bucket];
45fa9e29 413 i->bucket_previous = NULL;
9bf3b535
LP
414 if (n[new_bucket])
415 n[new_bucket]->bucket_previous = i;
416 n[new_bucket] = i;
45fa9e29
LP
417 }
418
419 if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
420 free(h->buckets);
421
422 h->buckets = n;
423 h->n_buckets = m;
9bf3b535
LP
424
425 memcpy(h->hash_key, nkey, HASH_KEY_SIZE);
45fa9e29 426
9700d698 427 return 1;
45fa9e29
LP
428}
429
923041cb
MS
430static int __hashmap_put(Hashmap *h, const void *key, void *value, unsigned hash) {
431 /* For when we know no such entry exists yet */
60918275 432
923041cb 433 struct hashmap_entry *e;
60918275 434
9700d698 435 if (resize_buckets(h) > 0)
a3b6fafe 436 hash = bucket_hash(h, key);
45fa9e29 437
39c2a6f1 438 if (h->from_pool)
b3dcf58e 439 e = mempool_alloc_tile(&hashmap_entry_pool);
39c2a6f1
LP
440 else
441 e = new(struct hashmap_entry, 1);
442
443 if (!e)
60918275
LP
444 return -ENOMEM;
445
446 e->key = key;
447 e->value = value;
448
101d8e63 449 link_entry(h, e, hash);
60918275 450
48507e66 451 return 1;
60918275
LP
452}
453
923041cb
MS
454int hashmap_put(Hashmap *h, const void *key, void *value) {
455 struct hashmap_entry *e;
456 unsigned hash;
457
458 assert(h);
459
460 hash = bucket_hash(h, key);
461 e = hash_scan(h, hash, key);
462 if (e) {
463 if (e->value == value)
464 return 0;
465 return -EEXIST;
466 }
467
468 return __hashmap_put(h, key, value, hash);
469}
470
3158713e
LP
471int hashmap_replace(Hashmap *h, const void *key, void *value) {
472 struct hashmap_entry *e;
473 unsigned hash;
474
475 assert(h);
476
a3b6fafe 477 hash = bucket_hash(h, key);
d99ae53a
LP
478 e = hash_scan(h, hash, key);
479 if (e) {
101d8e63 480 e->key = key;
3158713e
LP
481 e->value = value;
482 return 0;
483 }
484
923041cb 485 return __hashmap_put(h, key, value, hash);
3158713e
LP
486}
487
d99ae53a
LP
488int hashmap_update(Hashmap *h, const void *key, void *value) {
489 struct hashmap_entry *e;
490 unsigned hash;
491
492 assert(h);
493
a3b6fafe 494 hash = bucket_hash(h, key);
d99ae53a
LP
495 e = hash_scan(h, hash, key);
496 if (!e)
497 return -ENOENT;
498
499 e->value = value;
500 return 0;
501}
502
60918275
LP
503void* hashmap_get(Hashmap *h, const void *key) {
504 unsigned hash;
505 struct hashmap_entry *e;
506
507 if (!h)
508 return NULL;
509
a3b6fafe 510 hash = bucket_hash(h, key);
67f3c402
LP
511 e = hash_scan(h, hash, key);
512 if (!e)
60918275
LP
513 return NULL;
514
515 return e->value;
516}
517
d99ae53a
LP
518void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
519 unsigned hash;
520 struct hashmap_entry *e;
521
522 if (!h)
523 return NULL;
524
a3b6fafe 525 hash = bucket_hash(h, key);
d99ae53a
LP
526 e = hash_scan(h, hash, key);
527 if (!e)
528 return NULL;
529
530 if (key2)
531 *key2 = (void*) e->key;
532
533 return e->value;
534}
535
96342de6
LN
536bool hashmap_contains(Hashmap *h, const void *key) {
537 unsigned hash;
96342de6
LN
538
539 if (!h)
540 return false;
541
a3b6fafe
LP
542 hash = bucket_hash(h, key);
543 return !!hash_scan(h, hash, key);
96342de6
LN
544}
545
60918275
LP
546void* hashmap_remove(Hashmap *h, const void *key) {
547 struct hashmap_entry *e;
548 unsigned hash;
549 void *data;
550
551 if (!h)
552 return NULL;
553
a3b6fafe
LP
554 hash = bucket_hash(h, key);
555 e = hash_scan(h, hash, key);
556 if (!e)
60918275
LP
557 return NULL;
558
559 data = e->value;
560 remove_entry(h, e);
561
562 return data;
563}
564
c582a3b3
LP
565void* hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
566 struct hashmap_entry *e;
567 unsigned hash;
568 void *data;
569
570 if (!h) {
571 if (rkey)
572 *rkey = NULL;
573 return NULL;
574 }
575
576 hash = bucket_hash(h, key);
577 e = hash_scan(h, hash, key);
578 if (!e) {
579 if (rkey)
580 *rkey = NULL;
581 return NULL;
582 }
583
584 data = e->value;
585 if (rkey)
586 *rkey = (void*) e->key;
587
588 remove_entry(h, e);
589
590 return data;
591}
592
101d8e63
LP
593int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
594 struct hashmap_entry *e;
595 unsigned old_hash, new_hash;
596
597 if (!h)
598 return -ENOENT;
599
a3b6fafe
LP
600 old_hash = bucket_hash(h, old_key);
601 e = hash_scan(h, old_hash, old_key);
602 if (!e)
101d8e63
LP
603 return -ENOENT;
604
a3b6fafe 605 new_hash = bucket_hash(h, new_key);
101d8e63
LP
606 if (hash_scan(h, new_hash, new_key))
607 return -EEXIST;
608
609 unlink_entry(h, e, old_hash);
610
611 e->key = new_key;
612 e->value = value;
613
614 link_entry(h, e, new_hash);
615
616 return 0;
617}
618
8fe914ec
LP
619int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
620 struct hashmap_entry *e, *k;
621 unsigned old_hash, new_hash;
622
623 if (!h)
624 return -ENOENT;
625
a3b6fafe
LP
626 old_hash = bucket_hash(h, old_key);
627 e = hash_scan(h, old_hash, old_key);
628 if (!e)
8fe914ec
LP
629 return -ENOENT;
630
a3b6fafe
LP
631 new_hash = bucket_hash(h, new_key);
632 k = hash_scan(h, new_hash, new_key);
633 if (k)
8fe914ec
LP
634 if (e != k)
635 remove_entry(h, k);
636
637 unlink_entry(h, e, old_hash);
638
639 e->key = new_key;
640 e->value = value;
641
642 link_entry(h, e, new_hash);
643
644 return 0;
645}
646
3158713e
LP
647void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
648 struct hashmap_entry *e;
649 unsigned hash;
650
651 if (!h)
652 return NULL;
653
a3b6fafe 654 hash = bucket_hash(h, key);
3158713e 655
45fa9e29
LP
656 e = hash_scan(h, hash, key);
657 if (!e)
3158713e
LP
658 return NULL;
659
660 if (e->value != value)
661 return NULL;
662
663 remove_entry(h, e);
664
665 return value;
666}
667
034c6ed7 668void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
60918275
LP
669 struct hashmap_entry *e;
670
034c6ed7 671 assert(i);
60918275
LP
672
673 if (!h)
674 goto at_end;
675
034c6ed7 676 if (*i == ITERATOR_LAST)
60918275
LP
677 goto at_end;
678
034c6ed7 679 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
60918275
LP
680 goto at_end;
681
034c6ed7 682 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
60918275
LP
683
684 if (e->iterate_next)
034c6ed7 685 *i = (Iterator) e->iterate_next;
60918275 686 else
034c6ed7 687 *i = ITERATOR_LAST;
60918275
LP
688
689 if (key)
690 *key = e->key;
691
692 return e->value;
693
694at_end:
034c6ed7 695 *i = ITERATOR_LAST;
60918275
LP
696
697 if (key)
698 *key = NULL;
699
700 return NULL;
701}
702
60918275
LP
703void* hashmap_first(Hashmap *h) {
704
705 if (!h)
706 return NULL;
707
708 if (!h->iterate_list_head)
709 return NULL;
710
711 return h->iterate_list_head->value;
712}
713
2e4a6ff4
LP
714void* hashmap_first_key(Hashmap *h) {
715
716 if (!h)
717 return NULL;
718
719 if (!h->iterate_list_head)
720 return NULL;
721
722 return (void*) h->iterate_list_head->key;
723}
724
60918275
LP
725void* hashmap_steal_first(Hashmap *h) {
726 void *data;
727
728 if (!h)
729 return NULL;
730
731 if (!h->iterate_list_head)
732 return NULL;
733
734 data = h->iterate_list_head->value;
735 remove_entry(h, h->iterate_list_head);
736
737 return data;
738}
739
22be093f
LP
740void* hashmap_steal_first_key(Hashmap *h) {
741 void *key;
742
743 if (!h)
744 return NULL;
745
746 if (!h->iterate_list_head)
747 return NULL;
748
749 key = (void*) h->iterate_list_head->key;
750 remove_entry(h, h->iterate_list_head);
751
752 return key;
753}
754
60918275
LP
755unsigned hashmap_size(Hashmap *h) {
756
757 if (!h)
758 return 0;
759
760 return h->n_entries;
761}
762
45fa9e29
LP
763unsigned hashmap_buckets(Hashmap *h) {
764
765 if (!h)
766 return 0;
767
768 return h->n_buckets;
769}
770
60918275
LP
771bool hashmap_isempty(Hashmap *h) {
772
773 if (!h)
774 return true;
775
776 return h->n_entries == 0;
777}
91cdde8a
LP
778
779int hashmap_merge(Hashmap *h, Hashmap *other) {
780 struct hashmap_entry *e;
781
782 assert(h);
783
784 if (!other)
785 return 0;
786
787 for (e = other->iterate_list_head; e; e = e->iterate_next) {
788 int r;
789
a3b6fafe
LP
790 r = hashmap_put(h, e->key, e->value);
791 if (r < 0 && r != -EEXIST)
792 return r;
91cdde8a
LP
793 }
794
795 return 0;
796}
797
101d8e63
LP
798void hashmap_move(Hashmap *h, Hashmap *other) {
799 struct hashmap_entry *e, *n;
800
801 assert(h);
802
803 /* The same as hashmap_merge(), but every new item from other
804 * is moved to h. This function is guaranteed to succeed. */
805
806 if (!other)
807 return;
808
809 for (e = other->iterate_list_head; e; e = n) {
810 unsigned h_hash, other_hash;
811
812 n = e->iterate_next;
813
a3b6fafe 814 h_hash = bucket_hash(h, e->key);
101d8e63
LP
815 if (hash_scan(h, h_hash, e->key))
816 continue;
817
a3b6fafe 818 other_hash = bucket_hash(other, e->key);
101d8e63
LP
819 unlink_entry(other, e, other_hash);
820 link_entry(h, e, h_hash);
821 }
822}
823
824int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
825 unsigned h_hash, other_hash;
826 struct hashmap_entry *e;
827
101d8e63
LP
828 assert(h);
829
a3b6fafe 830 h_hash = bucket_hash(h, key);
101d8e63
LP
831 if (hash_scan(h, h_hash, key))
832 return -EEXIST;
833
bf3d3e2b
MS
834 if (!other)
835 return -ENOENT;
836
a3b6fafe 837 other_hash = bucket_hash(other, key);
45fa9e29
LP
838 e = hash_scan(other, other_hash, key);
839 if (!e)
101d8e63
LP
840 return -ENOENT;
841
842 unlink_entry(other, e, other_hash);
843 link_entry(h, e, h_hash);
844
845 return 0;
846}
847
91cdde8a
LP
848Hashmap *hashmap_copy(Hashmap *h) {
849 Hashmap *copy;
850
851 assert(h);
852
d5099efc 853 copy = hashmap_new(h->hash_ops);
45fa9e29 854 if (!copy)
91cdde8a
LP
855 return NULL;
856
857 if (hashmap_merge(copy, h) < 0) {
858 hashmap_free(copy);
859 return NULL;
860 }
861
862 return copy;
863}
db1413d7
KS
864
865char **hashmap_get_strv(Hashmap *h) {
866 char **sv;
867 Iterator it;
729e3769 868 char *item;
db1413d7
KS
869 int n;
870
729e3769
LP
871 sv = new(char*, h->n_entries+1);
872 if (!sv)
db1413d7
KS
873 return NULL;
874
875 n = 0;
729e3769
LP
876 HASHMAP_FOREACH(item, h, it)
877 sv[n++] = item;
db1413d7
KS
878 sv[n] = NULL;
879
880 return sv;
881}
3c1668da
LP
882
883void *hashmap_next(Hashmap *h, const void *key) {
884 unsigned hash;
885 struct hashmap_entry *e;
886
3c1668da
LP
887 assert(key);
888
889 if (!h)
890 return NULL;
891
a3b6fafe 892 hash = bucket_hash(h, key);
3c1668da
LP
893 e = hash_scan(h, hash, key);
894 if (!e)
895 return NULL;
896
897 e = e->iterate_next;
898 if (!e)
899 return NULL;
900
901 return e->value;
902}