]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/hashmap.c
Fix typo in statx macro (#7180)
[thirdparty/systemd.git] / src / basic / hashmap.c
CommitLineData
a7334b09
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
89439d4f 5 Copyright 2014 Michal Schmidt
a7334b09
LP
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
a7334b09
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
a7334b09 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
60918275 21#include <errno.h>
11c3a366 22#include <stdint.h>
d4510856 23#include <stdlib.h>
11c3a366 24#include <string.h>
60918275 25
b5efdb8a 26#include "alloc-util.h"
60918275
LP
27#include "hashmap.h"
28#include "macro.h"
b3dcf58e 29#include "mempool.h"
d4510856 30#include "process-util.h"
3df3e884 31#include "random-util.h"
d4510856
LP
32#include "set.h"
33#include "siphash24.h"
34#include "strv.h"
35#include "util.h"
60918275 36
349cc4a5 37#if ENABLE_DEBUG_HASHMAP
3d4db144 38#include <pthread.h>
2eec67ac
TA
39#include "list.h"
40#endif
41
89439d4f
MS
42/*
43 * Implementation of hashmaps.
44 * Addressing: open
45 * - uses less RAM compared to closed addressing (chaining), because
46 * our entries are small (especially in Sets, which tend to contain
47 * the majority of entries in systemd).
48 * Collision resolution: Robin Hood
49 * - tends to equalize displacement of entries from their optimal buckets.
50 * Probe sequence: linear
51 * - though theoretically worse than random probing/uniform hashing/double
52 * hashing, it is good for cache locality.
53 *
54 * References:
55 * Celis, P. 1986. Robin Hood Hashing.
56 * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
57 * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
58 * - The results are derived for random probing. Suggests deletion with
59 * tombstones and two mean-centered search methods. None of that works
60 * well for linear probing.
61 *
62 * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
63 * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
64 * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
65 * http://www.math.uu.se/~svante/papers/sj157.pdf
66 * - Applies to Robin Hood with linear probing. Contains remarks on
67 * the unsuitability of mean-centered search with linear probing.
68 *
69 * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
70 * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
71 * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
72 * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
73 * in a successful search), and Janson writes about displacement. C = d + 1.
74 *
75 * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
76 * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
77 * - Explanation of backward shift deletion with pictures.
78 *
79 * Khuong, P. 2013. The Other Robin Hood Hashing.
80 * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
81 * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
82 */
83
84/*
85 * XXX Ideas for improvement:
86 * For unordered hashmaps, randomize iteration order, similarly to Perl:
87 * http://blog.booking.com/hardening-perls-hash-function.html
88 */
89
90/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
91 * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
92#define INV_KEEP_FREE 5U
93
94/* Fields common to entries of all hashmap/set types */
95struct hashmap_base_entry {
60918275 96 const void *key;
89439d4f
MS
97};
98
99/* Entry types for specific hashmap/set types
100 * hashmap_base_entry must be at the beginning of each entry struct. */
101
102struct plain_hashmap_entry {
103 struct hashmap_base_entry b;
60918275 104 void *value;
60918275
LP
105};
106
89439d4f
MS
107struct ordered_hashmap_entry {
108 struct plain_hashmap_entry p;
109 unsigned iterate_next, iterate_previous;
110};
60918275 111
89439d4f
MS
112struct set_entry {
113 struct hashmap_base_entry b;
114};
45fa9e29 115
89439d4f
MS
116/* In several functions it is advantageous to have the hash table extended
117 * virtually by a couple of additional buckets. We reserve special index values
118 * for these "swap" buckets. */
119#define _IDX_SWAP_BEGIN (UINT_MAX - 3)
120#define IDX_PUT (_IDX_SWAP_BEGIN + 0)
121#define IDX_TMP (_IDX_SWAP_BEGIN + 1)
122#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2)
39c2a6f1 123
89439d4f
MS
124#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */
125#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */
126
127assert_cc(IDX_FIRST == _IDX_SWAP_END);
128assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
129
130/* Storage space for the "swap" buckets.
131 * All entry types can fit into a ordered_hashmap_entry. */
132struct swap_entries {
133 struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
60918275
LP
134};
135
89439d4f
MS
136/* Distance from Initial Bucket */
137typedef uint8_t dib_raw_t;
3ef11dcf
ZJS
138#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
139#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
140#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
141#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
89439d4f
MS
142
143#define DIB_FREE UINT_MAX
144
349cc4a5 145#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
146struct hashmap_debug_info {
147 LIST_FIELDS(struct hashmap_debug_info, debug_list);
148 unsigned max_entries; /* high watermark of n_entries */
149
150 /* who allocated this hashmap */
151 int line;
152 const char *file;
153 const char *func;
154
155 /* fields to detect modification while iterating */
156 unsigned put_count; /* counts puts into the hashmap */
157 unsigned rem_count; /* counts removals from hashmap */
158 unsigned last_rem_idx; /* remembers last removal index */
39c2a6f1
LP
159};
160
89439d4f
MS
161/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
162static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
4f1b3061 163static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER;
39c2a6f1 164
89439d4f 165#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
39c2a6f1 166
fc86aa0e 167#else /* !ENABLE_DEBUG_HASHMAP */
89439d4f 168#define HASHMAP_DEBUG_FIELDS
fc86aa0e 169#endif /* ENABLE_DEBUG_HASHMAP */
39c2a6f1 170
89439d4f
MS
171enum HashmapType {
172 HASHMAP_TYPE_PLAIN,
173 HASHMAP_TYPE_ORDERED,
174 HASHMAP_TYPE_SET,
175 _HASHMAP_TYPE_MAX
176};
39c2a6f1 177
89439d4f 178struct _packed_ indirect_storage {
1a39bc8c 179 void *storage; /* where buckets and DIBs are stored */
89439d4f
MS
180 uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */
181
182 unsigned n_entries; /* number of stored entries */
183 unsigned n_buckets; /* number of buckets */
184
185 unsigned idx_lowest_entry; /* Index below which all buckets are free.
186 Makes "while(hashmap_steal_first())" loops
187 O(n) instead of O(n^2) for unordered hashmaps. */
188 uint8_t _pad[3]; /* padding for the whole HashmapBase */
189 /* The bitfields in HashmapBase complete the alignment of the whole thing. */
190};
191
192struct direct_storage {
193 /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
194 * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
195 * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
1a39bc8c 196 uint8_t storage[sizeof(struct indirect_storage)];
89439d4f
MS
197};
198
199#define DIRECT_BUCKETS(entry_t) \
200 (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
201
202/* We should be able to store at least one entry directly. */
203assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
204
205/* We have 3 bits for n_direct_entries. */
206assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
207
208/* Hashmaps with directly stored entries all use this shared hash key.
209 * It's no big deal if the key is guessed, because there can be only
210 * a handful of directly stored entries in a hashmap. When a hashmap
211 * outgrows direct storage, it gets its own key for indirect storage. */
212static uint8_t shared_hash_key[HASH_KEY_SIZE];
213static bool shared_hash_key_initialized;
214
215/* Fields that all hashmap/set types must have */
216struct HashmapBase {
217 const struct hash_ops *hash_ops; /* hash and compare ops to use */
218
219 union _packed_ {
220 struct indirect_storage indirect; /* if has_indirect */
221 struct direct_storage direct; /* if !has_indirect */
222 };
223
224 enum HashmapType type:2; /* HASHMAP_TYPE_* */
225 bool has_indirect:1; /* whether indirect storage is used */
226 unsigned n_direct_entries:3; /* Number of entries in direct storage.
227 * Only valid if !has_indirect. */
228 bool from_pool:1; /* whether was allocated from mempool */
229 HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */
230};
231
232/* Specific hash types
233 * HashmapBase must be at the beginning of each hashmap struct. */
234
235struct Hashmap {
236 struct HashmapBase b;
237};
238
239struct OrderedHashmap {
240 struct HashmapBase b;
241 unsigned iterate_list_head, iterate_list_tail;
242};
243
244struct Set {
245 struct HashmapBase b;
246};
247
248DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8);
249DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
250/* No need for a separate Set pool */
251assert_cc(sizeof(Hashmap) == sizeof(Set));
252
253struct hashmap_type_info {
254 size_t head_size;
255 size_t entry_size;
256 struct mempool *mempool;
257 unsigned n_direct_buckets;
258};
259
260static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
261 [HASHMAP_TYPE_PLAIN] = {
262 .head_size = sizeof(Hashmap),
263 .entry_size = sizeof(struct plain_hashmap_entry),
264 .mempool = &hashmap_pool,
265 .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
266 },
267 [HASHMAP_TYPE_ORDERED] = {
268 .head_size = sizeof(OrderedHashmap),
269 .entry_size = sizeof(struct ordered_hashmap_entry),
270 .mempool = &ordered_hashmap_pool,
271 .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
272 },
273 [HASHMAP_TYPE_SET] = {
274 .head_size = sizeof(Set),
275 .entry_size = sizeof(struct set_entry),
276 .mempool = &hashmap_pool,
277 .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
278 },
279};
39c2a6f1 280
89439d4f
MS
281static unsigned n_buckets(HashmapBase *h) {
282 return h->has_indirect ? h->indirect.n_buckets
283 : hashmap_type_info[h->type].n_direct_buckets;
284}
285
286static unsigned n_entries(HashmapBase *h) {
287 return h->has_indirect ? h->indirect.n_entries
288 : h->n_direct_entries;
289}
290
291static void n_entries_inc(HashmapBase *h) {
292 if (h->has_indirect)
293 h->indirect.n_entries++;
294 else
295 h->n_direct_entries++;
296}
297
298static void n_entries_dec(HashmapBase *h) {
299 if (h->has_indirect)
300 h->indirect.n_entries--;
301 else
302 h->n_direct_entries--;
303}
304
1a39bc8c 305static void *storage_ptr(HashmapBase *h) {
89439d4f
MS
306 return h->has_indirect ? h->indirect.storage
307 : h->direct.storage;
308}
309
310static uint8_t *hash_key(HashmapBase *h) {
311 return h->has_indirect ? h->indirect.hash_key
312 : shared_hash_key;
313}
314
315static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
b826ab58 316 struct siphash state;
0cb3c286 317 uint64_t hash;
b826ab58 318
0cb3c286 319 siphash24_init(&state, hash_key(h));
b826ab58
TG
320
321 h->hash_ops->hash(p, &state);
322
933f9cae 323 hash = siphash24_finalize(&state);
0cb3c286
TG
324
325 return (unsigned) (hash % n_buckets(h));
9bf3b535 326}
89439d4f 327#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
9bf3b535
LP
328
329static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
330 static uint8_t current[HASH_KEY_SIZE];
331 static bool current_initialized = false;
332
333 /* Returns a hash function key to use. In order to keep things
334 * fast we will not generate a new key each time we allocate a
335 * new hash table. Instead, we'll just reuse the most recently
336 * generated one, except if we never generated one or when we
337 * are rehashing an entire hash table because we reached a
338 * fill level */
339
340 if (!current_initialized || !reuse_is_ok) {
341 random_bytes(current, sizeof(current));
342 current_initialized = true;
343 }
344
345 memcpy(hash_key, current, sizeof(current));
a3b6fafe
LP
346}
347
89439d4f
MS
348static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
349 return (struct hashmap_base_entry*)
1a39bc8c 350 ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
89439d4f
MS
351}
352
353static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
354 return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
355}
356
357static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
358 return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
359}
39c2a6f1 360
89439d4f
MS
361static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
362 return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
363}
39c2a6f1 364
89439d4f
MS
365static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
366 return &swap->e[idx - _IDX_SWAP_BEGIN];
367}
39c2a6f1 368
89439d4f
MS
369/* Returns a pointer to the bucket at index idx.
370 * Understands real indexes and swap indexes, hence "_virtual". */
371static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
372 unsigned idx) {
373 if (idx < _IDX_SWAP_BEGIN)
374 return bucket_at(h, idx);
375
376 if (idx < _IDX_SWAP_END)
377 return &bucket_at_swap(swap, idx)->p.b;
378
379 assert_not_reached("Invalid index");
380}
381
382static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
383 return (dib_raw_t*)
1a39bc8c 384 ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
89439d4f
MS
385}
386
387static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
388 return idx >= from ? idx - from
389 : n_buckets(h) + idx - from;
390}
391
392static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
393 unsigned initial_bucket;
394
395 if (raw_dib == DIB_RAW_FREE)
396 return DIB_FREE;
397
398 if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
399 return raw_dib;
400
401 /*
402 * Having an overflow DIB value is very unlikely. The hash function
403 * would have to be bad. For example, in a table of size 2^24 filled
404 * to load factor 0.9 the maximum observed DIB is only about 60.
405 * In theory (assuming I used Maxima correctly), for an infinite size
406 * hash table with load factor 0.8 the probability of a given entry
407 * having DIB > 40 is 1.9e-8.
408 * This returns the correct DIB value by recomputing the hash value in
409 * the unlikely case. XXX Hitting this case could be a hint to rehash.
410 */
411 initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
412 return bucket_distance(h, idx, initial_bucket);
413}
414
415static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
416 dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
417}
418
419static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
420 dib_raw_t *dibs;
421
422 dibs = dib_raw_ptr(h);
423
424 for ( ; idx < n_buckets(h); idx++)
425 if (dibs[idx] != DIB_RAW_FREE)
426 return idx;
427
428 return IDX_NIL;
429}
430
431static void bucket_mark_free(HashmapBase *h, unsigned idx) {
eccaf899 432 memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size);
89439d4f
MS
433 bucket_set_dib(h, idx, DIB_FREE);
434}
435
436static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
437 unsigned from, unsigned to) {
438 struct hashmap_base_entry *e_from, *e_to;
439
440 assert(from != to);
39c2a6f1 441
89439d4f
MS
442 e_from = bucket_at_virtual(h, swap, from);
443 e_to = bucket_at_virtual(h, swap, to);
444
445 memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
446
447 if (h->type == HASHMAP_TYPE_ORDERED) {
448 OrderedHashmap *lh = (OrderedHashmap*) h;
449 struct ordered_hashmap_entry *le, *le_to;
450
451 le_to = (struct ordered_hashmap_entry*) e_to;
452
453 if (le_to->iterate_next != IDX_NIL) {
454 le = (struct ordered_hashmap_entry*)
455 bucket_at_virtual(h, swap, le_to->iterate_next);
456 le->iterate_previous = to;
457 }
458
459 if (le_to->iterate_previous != IDX_NIL) {
460 le = (struct ordered_hashmap_entry*)
461 bucket_at_virtual(h, swap, le_to->iterate_previous);
462 le->iterate_next = to;
463 }
464
465 if (lh->iterate_list_head == from)
466 lh->iterate_list_head = to;
467 if (lh->iterate_list_tail == from)
468 lh->iterate_list_tail = to;
39c2a6f1 469 }
89439d4f 470}
60918275 471
89439d4f
MS
472static unsigned next_idx(HashmapBase *h, unsigned idx) {
473 return (idx + 1U) % n_buckets(h);
474}
60918275 475
89439d4f
MS
476static unsigned prev_idx(HashmapBase *h, unsigned idx) {
477 return (n_buckets(h) + idx - 1U) % n_buckets(h);
478}
60918275 479
89439d4f
MS
480static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
481 switch (h->type) {
45fa9e29 482
89439d4f
MS
483 case HASHMAP_TYPE_PLAIN:
484 case HASHMAP_TYPE_ORDERED:
485 return ((struct plain_hashmap_entry*)e)->value;
39c2a6f1 486
89439d4f
MS
487 case HASHMAP_TYPE_SET:
488 return (void*) e->key;
a3b6fafe 489
89439d4f
MS
490 default:
491 assert_not_reached("Unknown hashmap type");
492 }
60918275
LP
493}
494
89439d4f
MS
495static void base_remove_entry(HashmapBase *h, unsigned idx) {
496 unsigned left, right, prev, dib;
497 dib_raw_t raw_dib, *dibs;
45fa9e29 498
89439d4f
MS
499 dibs = dib_raw_ptr(h);
500 assert(dibs[idx] != DIB_RAW_FREE);
034c6ed7 501
349cc4a5 502#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
503 h->debug.rem_count++;
504 h->debug.last_rem_idx = idx;
505#endif
034c6ed7 506
89439d4f
MS
507 left = idx;
508 /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
509 for (right = next_idx(h, left); ; right = next_idx(h, right)) {
510 raw_dib = dibs[right];
4c701096 511 if (IN_SET(raw_dib, 0, DIB_RAW_FREE))
89439d4f
MS
512 break;
513
514 /* The buckets are not supposed to be all occupied and with DIB > 0.
515 * That would mean we could make everyone better off by shifting them
516 * backward. This scenario is impossible. */
517 assert(left != right);
518 }
034c6ed7 519
89439d4f
MS
520 if (h->type == HASHMAP_TYPE_ORDERED) {
521 OrderedHashmap *lh = (OrderedHashmap*) h;
522 struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
523
524 if (le->iterate_next != IDX_NIL)
525 ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
526 else
527 lh->iterate_list_tail = le->iterate_previous;
528
529 if (le->iterate_previous != IDX_NIL)
530 ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
531 else
532 lh->iterate_list_head = le->iterate_next;
533 }
534
535 /* Now shift all buckets in the interval (left, right) one step backwards */
536 for (prev = left, left = next_idx(h, left); left != right;
537 prev = left, left = next_idx(h, left)) {
538 dib = bucket_calculate_dib(h, left, dibs[left]);
539 assert(dib != 0);
540 bucket_move_entry(h, NULL, left, prev);
541 bucket_set_dib(h, prev, dib - 1);
542 }
543
544 bucket_mark_free(h, prev);
545 n_entries_dec(h);
034c6ed7 546}
89439d4f
MS
547#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
548
549static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
550 struct ordered_hashmap_entry *e;
551 unsigned idx;
034c6ed7 552
101d8e63 553 assert(h);
89439d4f
MS
554 assert(i);
555
556 if (i->idx == IDX_NIL)
557 goto at_end;
558
559 if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
560 goto at_end;
561
562 if (i->idx == IDX_FIRST) {
563 idx = h->iterate_list_head;
564 e = ordered_bucket_at(h, idx);
101d8e63 565 } else {
89439d4f
MS
566 idx = i->idx;
567 e = ordered_bucket_at(h, idx);
568 /*
569 * We allow removing the current entry while iterating, but removal may cause
570 * a backward shift. The next entry may thus move one bucket to the left.
571 * To detect when it happens, we remember the key pointer of the entry we were
572 * going to iterate next. If it does not match, there was a backward shift.
573 */
574 if (e->p.b.key != i->next_key) {
575 idx = prev_idx(HASHMAP_BASE(h), idx);
576 e = ordered_bucket_at(h, idx);
577 }
578 assert(e->p.b.key == i->next_key);
101d8e63 579 }
101d8e63 580
349cc4a5 581#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
582 i->prev_idx = idx;
583#endif
584
585 if (e->iterate_next != IDX_NIL) {
586 struct ordered_hashmap_entry *n;
587 i->idx = e->iterate_next;
588 n = ordered_bucket_at(h, i->idx);
589 i->next_key = n->p.b.key;
590 } else
591 i->idx = IDX_NIL;
592
593 return idx;
594
595at_end:
596 i->idx = IDX_NIL;
597 return IDX_NIL;
101d8e63
LP
598}
599
89439d4f
MS
600static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
601 unsigned idx;
602
60918275 603 assert(h);
89439d4f 604 assert(i);
60918275 605
89439d4f
MS
606 if (i->idx == IDX_NIL)
607 goto at_end;
60918275 608
89439d4f
MS
609 if (i->idx == IDX_FIRST) {
610 /* fast forward to the first occupied bucket */
611 if (h->has_indirect) {
612 i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
613 h->indirect.idx_lowest_entry = i->idx;
614 } else
615 i->idx = skip_free_buckets(h, 0);
616
617 if (i->idx == IDX_NIL)
618 goto at_end;
619 } else {
620 struct hashmap_base_entry *e;
621
622 assert(i->idx > 0);
60918275 623
89439d4f
MS
624 e = bucket_at(h, i->idx);
625 /*
626 * We allow removing the current entry while iterating, but removal may cause
627 * a backward shift. The next entry may thus move one bucket to the left.
628 * To detect when it happens, we remember the key pointer of the entry we were
629 * going to iterate next. If it does not match, there was a backward shift.
630 */
631 if (e->key != i->next_key)
632 e = bucket_at(h, --i->idx);
60918275 633
89439d4f
MS
634 assert(e->key == i->next_key);
635 }
636
637 idx = i->idx;
349cc4a5 638#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
639 i->prev_idx = idx;
640#endif
641
642 i->idx = skip_free_buckets(h, i->idx + 1);
643 if (i->idx != IDX_NIL)
644 i->next_key = bucket_at(h, i->idx)->key;
101d8e63 645 else
89439d4f
MS
646 i->idx = IDX_NIL;
647
648 return idx;
60918275 649
89439d4f
MS
650at_end:
651 i->idx = IDX_NIL;
652 return IDX_NIL;
60918275
LP
653}
654
89439d4f
MS
655static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
656 if (!h) {
657 i->idx = IDX_NIL;
658 return IDX_NIL;
659 }
101d8e63 660
349cc4a5 661#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
662 if (i->idx == IDX_FIRST) {
663 i->put_count = h->debug.put_count;
664 i->rem_count = h->debug.rem_count;
665 } else {
666 /* While iterating, must not add any new entries */
667 assert(i->put_count == h->debug.put_count);
668 /* ... or remove entries other than the current one */
669 assert(i->rem_count == h->debug.rem_count ||
670 (i->rem_count == h->debug.rem_count - 1 &&
671 i->prev_idx == h->debug.last_rem_idx));
672 /* Reset our removals counter */
673 i->rem_count = h->debug.rem_count;
674 }
675#endif
101d8e63 676
89439d4f
MS
677 return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
678 : hashmap_iterate_in_internal_order(h, i);
679}
39c2a6f1 680
8927b1da 681bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
89439d4f
MS
682 struct hashmap_base_entry *e;
683 void *data;
684 unsigned idx;
685
686 idx = hashmap_iterate_entry(h, i);
687 if (idx == IDX_NIL) {
8927b1da
DH
688 if (value)
689 *value = NULL;
89439d4f
MS
690 if (key)
691 *key = NULL;
692
8927b1da 693 return false;
89439d4f
MS
694 }
695
696 e = bucket_at(h, idx);
697 data = entry_value(h, e);
8927b1da
DH
698 if (value)
699 *value = data;
89439d4f
MS
700 if (key)
701 *key = e->key;
702
8927b1da 703 return true;
101d8e63
LP
704}
705
8927b1da
DH
706bool set_iterate(Set *s, Iterator *i, void **value) {
707 return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL);
89439d4f 708}
60918275 709
89439d4f
MS
710#define HASHMAP_FOREACH_IDX(idx, h, i) \
711 for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
712 (idx != IDX_NIL); \
713 (idx) = hashmap_iterate_entry((h), &(i)))
714
715static void reset_direct_storage(HashmapBase *h) {
716 const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
717 void *p;
718
719 assert(!h->has_indirect);
720
721 p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
722 memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
723}
724
3ef11dcf 725static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
89439d4f
MS
726 HashmapBase *h;
727 const struct hashmap_type_info *hi = &hashmap_type_info[type];
728 bool use_pool;
729
730 use_pool = is_main_thread();
731
732 h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
67f3c402 733
60918275 734 if (!h)
89439d4f
MS
735 return NULL;
736
737 h->type = type;
738 h->from_pool = use_pool;
739 h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
740
741 if (type == HASHMAP_TYPE_ORDERED) {
742 OrderedHashmap *lh = (OrderedHashmap*)h;
743 lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
744 }
745
746 reset_direct_storage(h);
60918275 747
89439d4f
MS
748 if (!shared_hash_key_initialized) {
749 random_bytes(shared_hash_key, sizeof(shared_hash_key));
750 shared_hash_key_initialized= true;
751 }
752
349cc4a5 753#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
754 h->debug.func = func;
755 h->debug.file = file;
756 h->debug.line = line;
4f1b3061
TG
757 assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
758 LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
759 assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
89439d4f
MS
760#endif
761
762 return h;
763}
60918275 764
89439d4f 765Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
3ef11dcf 766 return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
89439d4f
MS
767}
768
769OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
3ef11dcf 770 return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
89439d4f
MS
771}
772
773Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
3ef11dcf 774 return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
89439d4f
MS
775}
776
777static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
3ef11dcf 778 enum HashmapType type HASHMAP_DEBUG_PARAMS) {
89439d4f
MS
779 HashmapBase *q;
780
781 assert(h);
782
783 if (*h)
784 return 0;
785
3ef11dcf 786 q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
89439d4f
MS
787 if (!q)
788 return -ENOMEM;
789
790 *h = q;
791 return 0;
792}
793
794int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
3ef11dcf 795 return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
89439d4f
MS
796}
797
798int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
3ef11dcf 799 return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
89439d4f
MS
800}
801
802int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
3ef11dcf 803 return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
89439d4f
MS
804}
805
806static void hashmap_free_no_clear(HashmapBase *h) {
807 assert(!h->has_indirect);
808 assert(!h->n_direct_entries);
809
349cc4a5 810#if ENABLE_DEBUG_HASHMAP
4f1b3061 811 assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
89439d4f 812 LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
4f1b3061 813 assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
89439d4f 814#endif
45fa9e29 815
39c2a6f1 816 if (h->from_pool)
89439d4f 817 mempool_free_tile(hashmap_type_info[h->type].mempool, h);
39c2a6f1
LP
818 else
819 free(h);
60918275
LP
820}
821
cfe561a4 822HashmapBase *internal_hashmap_free(HashmapBase *h) {
89439d4f
MS
823
824 /* Free the hashmap, but nothing in it */
825
cfe561a4
DH
826 if (h) {
827 internal_hashmap_clear(h);
828 hashmap_free_no_clear(h);
829 }
89439d4f 830
cfe561a4 831 return NULL;
89439d4f
MS
832}
833
cfe561a4 834HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
67f3c402
LP
835
836 /* Free the hashmap and all data objects in it, but not the
837 * keys */
838
cfe561a4
DH
839 if (h) {
840 internal_hashmap_clear_free(h);
841 hashmap_free_no_clear(h);
842 }
61b1477c 843
cfe561a4 844 return NULL;
449ddb2d
LP
845}
846
cfe561a4 847Hashmap *hashmap_free_free_free(Hashmap *h) {
fabe5c0e
LP
848
849 /* Free the hashmap and all data and key objects in it */
850
cfe561a4
DH
851 if (h) {
852 hashmap_clear_free_free(h);
853 hashmap_free_no_clear(HASHMAP_BASE(h));
854 }
fabe5c0e 855
cfe561a4 856 return NULL;
fabe5c0e
LP
857}
858
89439d4f 859void internal_hashmap_clear(HashmapBase *h) {
11dd41ce
LP
860 if (!h)
861 return;
862
89439d4f
MS
863 if (h->has_indirect) {
864 free(h->indirect.storage);
865 h->has_indirect = false;
866 }
867
868 h->n_direct_entries = 0;
869 reset_direct_storage(h);
870
871 if (h->type == HASHMAP_TYPE_ORDERED) {
872 OrderedHashmap *lh = (OrderedHashmap*) h;
873 lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
874 }
11dd41ce
LP
875}
876
89439d4f
MS
877void internal_hashmap_clear_free(HashmapBase *h) {
878 unsigned idx;
9946996c 879
61b1477c
LP
880 if (!h)
881 return;
9946996c 882
89439d4f
MS
883 for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
884 idx = skip_free_buckets(h, idx + 1))
885 free(entry_value(h, bucket_at(h, idx)));
886
887 internal_hashmap_clear(h);
9946996c
LP
888}
889
fabe5c0e 890void hashmap_clear_free_free(Hashmap *h) {
89439d4f
MS
891 unsigned idx;
892
fabe5c0e
LP
893 if (!h)
894 return;
895
89439d4f
MS
896 for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
897 idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
898 struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
899 free((void*)e->b.key);
900 free(e->value);
fabe5c0e 901 }
89439d4f
MS
902
903 internal_hashmap_clear(HASHMAP_BASE(h));
fabe5c0e
LP
904}
905
89439d4f
MS
906static int resize_buckets(HashmapBase *h, unsigned entries_add);
907
908/*
909 * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
910 * Performs Robin Hood swaps as it goes. The entry to put must be placed
911 * by the caller into swap slot IDX_PUT.
912 * If used for in-place resizing, may leave a displaced entry in swap slot
913 * IDX_PUT. Caller must rehash it next.
914 * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
915 * false otherwise.
916 */
917static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
918 struct swap_entries *swap) {
919 dib_raw_t raw_dib, *dibs;
920 unsigned dib, distance;
921
349cc4a5 922#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
923 h->debug.put_count++;
924#endif
925
926 dibs = dib_raw_ptr(h);
927
928 for (distance = 0; ; distance++) {
929 raw_dib = dibs[idx];
3742095b 930 if (IN_SET(raw_dib, DIB_RAW_FREE, DIB_RAW_REHASH)) {
89439d4f
MS
931 if (raw_dib == DIB_RAW_REHASH)
932 bucket_move_entry(h, swap, idx, IDX_TMP);
933
934 if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
935 h->indirect.idx_lowest_entry = idx;
60918275 936
89439d4f
MS
937 bucket_set_dib(h, idx, distance);
938 bucket_move_entry(h, swap, IDX_PUT, idx);
939 if (raw_dib == DIB_RAW_REHASH) {
940 bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
941 return true;
942 }
60918275 943
89439d4f
MS
944 return false;
945 }
946
947 dib = bucket_calculate_dib(h, idx, raw_dib);
948
949 if (dib < distance) {
950 /* Found a wealthier entry. Go Robin Hood! */
89439d4f
MS
951 bucket_set_dib(h, idx, distance);
952
953 /* swap the entries */
954 bucket_move_entry(h, swap, idx, IDX_TMP);
955 bucket_move_entry(h, swap, IDX_PUT, idx);
956 bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
957
958 distance = dib;
959 }
960
961 idx = next_idx(h, idx);
962 }
60918275
LP
963}
964
89439d4f
MS
965/*
966 * Puts an entry into a hashmap, boldly - no check whether key already exists.
967 * The caller must place the entry (only its key and value, not link indexes)
968 * in swap slot IDX_PUT.
969 * Caller must ensure: the key does not exist yet in the hashmap.
970 * that resize is not needed if !may_resize.
971 * Returns: 1 if entry was put successfully.
972 * -ENOMEM if may_resize==true and resize failed with -ENOMEM.
973 * Cannot return -ENOMEM if !may_resize.
974 */
975static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
976 struct swap_entries *swap, bool may_resize) {
977 struct ordered_hashmap_entry *new_entry;
978 int r;
979
980 assert(idx < n_buckets(h));
981
982 new_entry = bucket_at_swap(swap, IDX_PUT);
983
984 if (may_resize) {
985 r = resize_buckets(h, 1);
986 if (r < 0)
987 return r;
988 if (r > 0)
989 idx = bucket_hash(h, new_entry->p.b.key);
990 }
991 assert(n_entries(h) < n_buckets(h));
992
993 if (h->type == HASHMAP_TYPE_ORDERED) {
994 OrderedHashmap *lh = (OrderedHashmap*) h;
995
996 new_entry->iterate_next = IDX_NIL;
997 new_entry->iterate_previous = lh->iterate_list_tail;
998
999 if (lh->iterate_list_tail != IDX_NIL) {
1000 struct ordered_hashmap_entry *old_tail;
1001
1002 old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
1003 assert(old_tail->iterate_next == IDX_NIL);
1004 old_tail->iterate_next = IDX_PUT;
1005 }
1006
1007 lh->iterate_list_tail = IDX_PUT;
1008 if (lh->iterate_list_head == IDX_NIL)
1009 lh->iterate_list_head = IDX_PUT;
1010 }
1011
1012 assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
1013
1014 n_entries_inc(h);
349cc4a5 1015#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
1016 h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
1017#endif
1018
1019 return 1;
1020}
1021#define hashmap_put_boldly(h, idx, swap, may_resize) \
1022 hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
1023
1024/*
1025 * Returns 0 if resize is not needed.
f131770b 1026 * 1 if successfully resized.
89439d4f
MS
1027 * -ENOMEM on allocation failure.
1028 */
1029static int resize_buckets(HashmapBase *h, unsigned entries_add) {
1030 struct swap_entries swap;
1a39bc8c 1031 void *new_storage;
89439d4f
MS
1032 dib_raw_t *old_dibs, *new_dibs;
1033 const struct hashmap_type_info *hi;
1034 unsigned idx, optimal_idx;
1035 unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
1036 uint8_t new_shift;
1037 bool rehash_next;
45fa9e29
LP
1038
1039 assert(h);
1040
89439d4f
MS
1041 hi = &hashmap_type_info[h->type];
1042 new_n_entries = n_entries(h) + entries_add;
e4c691b5
MS
1043
1044 /* overflow? */
89439d4f 1045 if (_unlikely_(new_n_entries < entries_add))
e4c691b5
MS
1046 return -ENOMEM;
1047
89439d4f
MS
1048 /* For direct storage we allow 100% load, because it's tiny. */
1049 if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
9700d698 1050 return 0;
45fa9e29 1051
89439d4f
MS
1052 /*
1053 * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
1054 * From it follows: m = n + n/(INV_KEEP_FREE - 1)
1055 */
1056 new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
1057 /* overflow? */
1058 if (_unlikely_(new_n_buckets < new_n_entries))
9700d698 1059 return -ENOMEM;
45fa9e29 1060
89439d4f
MS
1061 if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
1062 return -ENOMEM;
a3b6fafe 1063
89439d4f 1064 old_n_buckets = n_buckets(h);
45fa9e29 1065
89439d4f
MS
1066 if (_likely_(new_n_buckets <= old_n_buckets))
1067 return 0;
45fa9e29 1068
89439d4f
MS
1069 new_shift = log2u_round_up(MAX(
1070 new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
1071 2 * sizeof(struct direct_storage)));
45fa9e29 1072
89439d4f
MS
1073 /* Realloc storage (buckets and DIB array). */
1074 new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
1075 1U << new_shift);
1076 if (!new_storage)
1077 return -ENOMEM;
45fa9e29 1078
89439d4f
MS
1079 /* Must upgrade direct to indirect storage. */
1080 if (!h->has_indirect) {
1081 memcpy(new_storage, h->direct.storage,
1082 old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
1083 h->indirect.n_entries = h->n_direct_entries;
1084 h->indirect.idx_lowest_entry = 0;
1085 h->n_direct_entries = 0;
1086 }
45fa9e29 1087
89439d4f
MS
1088 /* Get a new hash key. If we've just upgraded to indirect storage,
1089 * allow reusing a previously generated key. It's still a different key
1090 * from the shared one that we used for direct storage. */
1091 get_hash_key(h->indirect.hash_key, !h->has_indirect);
1092
1093 h->has_indirect = true;
1094 h->indirect.storage = new_storage;
1095 h->indirect.n_buckets = (1U << new_shift) /
1096 (hi->entry_size + sizeof(dib_raw_t));
1097
1a39bc8c 1098 old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets);
89439d4f
MS
1099 new_dibs = dib_raw_ptr(h);
1100
1101 /*
1102 * Move the DIB array to the new place, replacing valid DIB values with
1103 * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
1104 * Note: Overlap is not possible, because we have at least doubled the
1105 * number of buckets and dib_raw_t is smaller than any entry type.
1106 */
1107 for (idx = 0; idx < old_n_buckets; idx++) {
1108 assert(old_dibs[idx] != DIB_RAW_REHASH);
1109 new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
1110 : DIB_RAW_REHASH;
45fa9e29
LP
1111 }
1112
89439d4f 1113 /* Zero the area of newly added entries (including the old DIB area) */
eccaf899 1114 memzero(bucket_at(h, old_n_buckets),
89439d4f 1115 (n_buckets(h) - old_n_buckets) * hi->entry_size);
45fa9e29 1116
89439d4f
MS
1117 /* The upper half of the new DIB array needs initialization */
1118 memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
1119 (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
9bf3b535 1120
89439d4f
MS
1121 /* Rehash entries that need it */
1122 n_rehashed = 0;
1123 for (idx = 0; idx < old_n_buckets; idx++) {
1124 if (new_dibs[idx] != DIB_RAW_REHASH)
1125 continue;
45fa9e29 1126
89439d4f 1127 optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
45fa9e29 1128
89439d4f
MS
1129 /*
1130 * Not much to do if by luck the entry hashes to its current
1131 * location. Just set its DIB.
1132 */
1133 if (optimal_idx == idx) {
1134 new_dibs[idx] = 0;
1135 n_rehashed++;
1136 continue;
1137 }
1138
1139 new_dibs[idx] = DIB_RAW_FREE;
1140 bucket_move_entry(h, &swap, idx, IDX_PUT);
1141 /* bucket_move_entry does not clear the source */
eccaf899 1142 memzero(bucket_at(h, idx), hi->entry_size);
89439d4f
MS
1143
1144 do {
1145 /*
1146 * Find the new bucket for the current entry. This may make
1147 * another entry homeless and load it into IDX_PUT.
1148 */
1149 rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
1150 n_rehashed++;
1151
1152 /* Did the current entry displace another one? */
1153 if (rehash_next)
1154 optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
1155 } while (rehash_next);
1156 }
60918275 1157
89439d4f 1158 assert(n_rehashed == n_entries(h));
60918275 1159
89439d4f
MS
1160 return 1;
1161}
45fa9e29 1162
89439d4f
MS
1163/*
1164 * Finds an entry with a matching key
1165 * Returns: index of the found entry, or IDX_NIL if not found.
1166 */
1167static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
1168 struct hashmap_base_entry *e;
1169 unsigned dib, distance;
1170 dib_raw_t *dibs = dib_raw_ptr(h);
39c2a6f1 1171
89439d4f 1172 assert(idx < n_buckets(h));
60918275 1173
89439d4f
MS
1174 for (distance = 0; ; distance++) {
1175 if (dibs[idx] == DIB_RAW_FREE)
1176 return IDX_NIL;
60918275 1177
89439d4f 1178 dib = bucket_calculate_dib(h, idx, dibs[idx]);
60918275 1179
89439d4f
MS
1180 if (dib < distance)
1181 return IDX_NIL;
1182 if (dib == distance) {
1183 e = bucket_at(h, idx);
1184 if (h->hash_ops->compare(e->key, key) == 0)
1185 return idx;
1186 }
1187
1188 idx = next_idx(h, idx);
1189 }
60918275 1190}
89439d4f 1191#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
60918275 1192
923041cb 1193int hashmap_put(Hashmap *h, const void *key, void *value) {
89439d4f
MS
1194 struct swap_entries swap;
1195 struct plain_hashmap_entry *e;
1196 unsigned hash, idx;
923041cb
MS
1197
1198 assert(h);
1199
1200 hash = bucket_hash(h, key);
89439d4f
MS
1201 idx = bucket_scan(h, hash, key);
1202 if (idx != IDX_NIL) {
1203 e = plain_bucket_at(h, idx);
923041cb
MS
1204 if (e->value == value)
1205 return 0;
1206 return -EEXIST;
1207 }
1208
89439d4f
MS
1209 e = &bucket_at_swap(&swap, IDX_PUT)->p;
1210 e->b.key = key;
1211 e->value = value;
1212 return hashmap_put_boldly(h, hash, &swap, true);
1213}
1214
1215int set_put(Set *s, const void *key) {
1216 struct swap_entries swap;
1217 struct hashmap_base_entry *e;
1218 unsigned hash, idx;
1219
1220 assert(s);
1221
1222 hash = bucket_hash(s, key);
1223 idx = bucket_scan(s, hash, key);
1224 if (idx != IDX_NIL)
1225 return 0;
1226
1227 e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
1228 e->key = key;
1229 return hashmap_put_boldly(s, hash, &swap, true);
923041cb
MS
1230}
1231
3158713e 1232int hashmap_replace(Hashmap *h, const void *key, void *value) {
89439d4f
MS
1233 struct swap_entries swap;
1234 struct plain_hashmap_entry *e;
1235 unsigned hash, idx;
3158713e
LP
1236
1237 assert(h);
1238
a3b6fafe 1239 hash = bucket_hash(h, key);
89439d4f
MS
1240 idx = bucket_scan(h, hash, key);
1241 if (idx != IDX_NIL) {
1242 e = plain_bucket_at(h, idx);
349cc4a5 1243#if ENABLE_DEBUG_HASHMAP
89439d4f
MS
1244 /* Although the key is equal, the key pointer may have changed,
1245 * and this would break our assumption for iterating. So count
1246 * this operation as incompatible with iteration. */
1247 if (e->b.key != key) {
1248 h->b.debug.put_count++;
1249 h->b.debug.rem_count++;
1250 h->b.debug.last_rem_idx = idx;
1251 }
1252#endif
1253 e->b.key = key;
3158713e
LP
1254 e->value = value;
1255 return 0;
1256 }
1257
89439d4f
MS
1258 e = &bucket_at_swap(&swap, IDX_PUT)->p;
1259 e->b.key = key;
1260 e->value = value;
1261 return hashmap_put_boldly(h, hash, &swap, true);
3158713e
LP
1262}
1263
d99ae53a 1264int hashmap_update(Hashmap *h, const void *key, void *value) {
89439d4f
MS
1265 struct plain_hashmap_entry *e;
1266 unsigned hash, idx;
d99ae53a
LP
1267
1268 assert(h);
1269
a3b6fafe 1270 hash = bucket_hash(h, key);
89439d4f
MS
1271 idx = bucket_scan(h, hash, key);
1272 if (idx == IDX_NIL)
d99ae53a
LP
1273 return -ENOENT;
1274
89439d4f 1275 e = plain_bucket_at(h, idx);
d99ae53a
LP
1276 e->value = value;
1277 return 0;
1278}
1279
89439d4f
MS
1280void *internal_hashmap_get(HashmapBase *h, const void *key) {
1281 struct hashmap_base_entry *e;
1282 unsigned hash, idx;
60918275
LP
1283
1284 if (!h)
1285 return NULL;
1286
a3b6fafe 1287 hash = bucket_hash(h, key);
89439d4f
MS
1288 idx = bucket_scan(h, hash, key);
1289 if (idx == IDX_NIL)
60918275
LP
1290 return NULL;
1291
89439d4f
MS
1292 e = bucket_at(h, idx);
1293 return entry_value(h, e);
60918275
LP
1294}
1295
89439d4f
MS
1296void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
1297 struct plain_hashmap_entry *e;
1298 unsigned hash, idx;
d99ae53a
LP
1299
1300 if (!h)
1301 return NULL;
1302
a3b6fafe 1303 hash = bucket_hash(h, key);
89439d4f
MS
1304 idx = bucket_scan(h, hash, key);
1305 if (idx == IDX_NIL)
d99ae53a
LP
1306 return NULL;
1307
89439d4f 1308 e = plain_bucket_at(h, idx);
d99ae53a 1309 if (key2)
89439d4f 1310 *key2 = (void*) e->b.key;
d99ae53a
LP
1311
1312 return e->value;
1313}
1314
89439d4f 1315bool internal_hashmap_contains(HashmapBase *h, const void *key) {
96342de6 1316 unsigned hash;
96342de6
LN
1317
1318 if (!h)
1319 return false;
1320
a3b6fafe 1321 hash = bucket_hash(h, key);
89439d4f 1322 return bucket_scan(h, hash, key) != IDX_NIL;
96342de6
LN
1323}
1324
89439d4f
MS
1325void *internal_hashmap_remove(HashmapBase *h, const void *key) {
1326 struct hashmap_base_entry *e;
1327 unsigned hash, idx;
60918275
LP
1328 void *data;
1329
1330 if (!h)
1331 return NULL;
1332
a3b6fafe 1333 hash = bucket_hash(h, key);
89439d4f
MS
1334 idx = bucket_scan(h, hash, key);
1335 if (idx == IDX_NIL)
60918275
LP
1336 return NULL;
1337
89439d4f
MS
1338 e = bucket_at(h, idx);
1339 data = entry_value(h, e);
1340 remove_entry(h, idx);
60918275
LP
1341
1342 return data;
1343}
1344
89439d4f
MS
1345void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
1346 struct plain_hashmap_entry *e;
1347 unsigned hash, idx;
c582a3b3
LP
1348 void *data;
1349
1350 if (!h) {
1351 if (rkey)
1352 *rkey = NULL;
1353 return NULL;
1354 }
1355
1356 hash = bucket_hash(h, key);
89439d4f
MS
1357 idx = bucket_scan(h, hash, key);
1358 if (idx == IDX_NIL) {
c582a3b3
LP
1359 if (rkey)
1360 *rkey = NULL;
1361 return NULL;
1362 }
1363
89439d4f 1364 e = plain_bucket_at(h, idx);
c582a3b3
LP
1365 data = e->value;
1366 if (rkey)
89439d4f 1367 *rkey = (void*) e->b.key;
c582a3b3 1368
89439d4f 1369 remove_entry(h, idx);
c582a3b3
LP
1370
1371 return data;
1372}
1373
101d8e63 1374int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
89439d4f
MS
1375 struct swap_entries swap;
1376 struct plain_hashmap_entry *e;
1377 unsigned old_hash, new_hash, idx;
101d8e63
LP
1378
1379 if (!h)
1380 return -ENOENT;
1381
a3b6fafe 1382 old_hash = bucket_hash(h, old_key);
89439d4f
MS
1383 idx = bucket_scan(h, old_hash, old_key);
1384 if (idx == IDX_NIL)
101d8e63
LP
1385 return -ENOENT;
1386
a3b6fafe 1387 new_hash = bucket_hash(h, new_key);
89439d4f 1388 if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
101d8e63
LP
1389 return -EEXIST;
1390
89439d4f 1391 remove_entry(h, idx);
101d8e63 1392
89439d4f
MS
1393 e = &bucket_at_swap(&swap, IDX_PUT)->p;
1394 e->b.key = new_key;
101d8e63 1395 e->value = value;
89439d4f
MS
1396 assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
1397
1398 return 0;
1399}
1400
1401int set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
1402 struct swap_entries swap;
1403 struct hashmap_base_entry *e;
1404 unsigned old_hash, new_hash, idx;
101d8e63 1405
89439d4f
MS
1406 if (!s)
1407 return -ENOENT;
1408
1409 old_hash = bucket_hash(s, old_key);
1410 idx = bucket_scan(s, old_hash, old_key);
1411 if (idx == IDX_NIL)
1412 return -ENOENT;
1413
1414 new_hash = bucket_hash(s, new_key);
1415 if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
1416 return -EEXIST;
1417
1418 remove_entry(s, idx);
1419
1420 e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
1421 e->key = new_key;
1422 assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
101d8e63
LP
1423
1424 return 0;
1425}
1426
8fe914ec 1427int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
89439d4f
MS
1428 struct swap_entries swap;
1429 struct plain_hashmap_entry *e;
1430 unsigned old_hash, new_hash, idx_old, idx_new;
8fe914ec
LP
1431
1432 if (!h)
1433 return -ENOENT;
1434
a3b6fafe 1435 old_hash = bucket_hash(h, old_key);
89439d4f
MS
1436 idx_old = bucket_scan(h, old_hash, old_key);
1437 if (idx_old == IDX_NIL)
8fe914ec
LP
1438 return -ENOENT;
1439
89439d4f 1440 old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
8fe914ec 1441
89439d4f
MS
1442 new_hash = bucket_hash(h, new_key);
1443 idx_new = bucket_scan(h, new_hash, new_key);
1444 if (idx_new != IDX_NIL)
1445 if (idx_old != idx_new) {
1446 remove_entry(h, idx_new);
1447 /* Compensate for a possible backward shift. */
1448 if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
1449 idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
1450 assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
1451 }
1452
1453 remove_entry(h, idx_old);
1454
1455 e = &bucket_at_swap(&swap, IDX_PUT)->p;
1456 e->b.key = new_key;
8fe914ec 1457 e->value = value;
89439d4f 1458 assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
8fe914ec
LP
1459
1460 return 0;
1461}
1462
89439d4f
MS
1463void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
1464 struct plain_hashmap_entry *e;
1465 unsigned hash, idx;
3158713e
LP
1466
1467 if (!h)
1468 return NULL;
1469
a3b6fafe 1470 hash = bucket_hash(h, key);
89439d4f
MS
1471 idx = bucket_scan(h, hash, key);
1472 if (idx == IDX_NIL)
3158713e
LP
1473 return NULL;
1474
89439d4f 1475 e = plain_bucket_at(h, idx);
3158713e
LP
1476 if (e->value != value)
1477 return NULL;
1478
89439d4f 1479 remove_entry(h, idx);
3158713e
LP
1480
1481 return value;
1482}
1483
89439d4f
MS
1484static unsigned find_first_entry(HashmapBase *h) {
1485 Iterator i = ITERATOR_FIRST;
60918275 1486
89439d4f
MS
1487 if (!h || !n_entries(h))
1488 return IDX_NIL;
60918275 1489
89439d4f 1490 return hashmap_iterate_entry(h, &i);
60918275
LP
1491}
1492
89439d4f
MS
1493void *internal_hashmap_first(HashmapBase *h) {
1494 unsigned idx;
60918275 1495
89439d4f
MS
1496 idx = find_first_entry(h);
1497 if (idx == IDX_NIL)
60918275
LP
1498 return NULL;
1499
89439d4f 1500 return entry_value(h, bucket_at(h, idx));
60918275
LP
1501}
1502
89439d4f
MS
1503void *internal_hashmap_first_key(HashmapBase *h) {
1504 struct hashmap_base_entry *e;
1505 unsigned idx;
2e4a6ff4 1506
89439d4f
MS
1507 idx = find_first_entry(h);
1508 if (idx == IDX_NIL)
2e4a6ff4
LP
1509 return NULL;
1510
89439d4f
MS
1511 e = bucket_at(h, idx);
1512 return (void*) e->key;
2e4a6ff4
LP
1513}
1514
89439d4f
MS
1515void *internal_hashmap_steal_first(HashmapBase *h) {
1516 struct hashmap_base_entry *e;
60918275 1517 void *data;
89439d4f 1518 unsigned idx;
60918275 1519
89439d4f
MS
1520 idx = find_first_entry(h);
1521 if (idx == IDX_NIL)
60918275
LP
1522 return NULL;
1523
89439d4f
MS
1524 e = bucket_at(h, idx);
1525 data = entry_value(h, e);
1526 remove_entry(h, idx);
60918275
LP
1527
1528 return data;
1529}
1530
89439d4f
MS
1531void *internal_hashmap_steal_first_key(HashmapBase *h) {
1532 struct hashmap_base_entry *e;
22be093f 1533 void *key;
89439d4f 1534 unsigned idx;
22be093f 1535
89439d4f
MS
1536 idx = find_first_entry(h);
1537 if (idx == IDX_NIL)
22be093f
LP
1538 return NULL;
1539
89439d4f
MS
1540 e = bucket_at(h, idx);
1541 key = (void*) e->key;
1542 remove_entry(h, idx);
22be093f
LP
1543
1544 return key;
1545}
1546
89439d4f 1547unsigned internal_hashmap_size(HashmapBase *h) {
60918275
LP
1548
1549 if (!h)
1550 return 0;
1551
89439d4f 1552 return n_entries(h);
60918275
LP
1553}
1554
89439d4f 1555unsigned internal_hashmap_buckets(HashmapBase *h) {
45fa9e29
LP
1556
1557 if (!h)
1558 return 0;
1559
89439d4f 1560 return n_buckets(h);
45fa9e29
LP
1561}
1562
89439d4f
MS
1563int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
1564 Iterator i;
1565 unsigned idx;
60918275 1566
89439d4f 1567 assert(h);
60918275 1568
89439d4f
MS
1569 HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
1570 struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
1571 int r;
91cdde8a 1572
89439d4f
MS
1573 r = hashmap_put(h, pe->b.key, pe->value);
1574 if (r < 0 && r != -EEXIST)
1575 return r;
1576 }
91cdde8a 1577
89439d4f
MS
1578 return 0;
1579}
91cdde8a 1580
89439d4f
MS
1581int set_merge(Set *s, Set *other) {
1582 Iterator i;
1583 unsigned idx;
91cdde8a 1584
89439d4f
MS
1585 assert(s);
1586
1587 HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
1588 struct set_entry *se = set_bucket_at(other, idx);
91cdde8a
LP
1589 int r;
1590
89439d4f
MS
1591 r = set_put(s, se->b.key);
1592 if (r < 0)
a3b6fafe 1593 return r;
91cdde8a
LP
1594 }
1595
1596 return 0;
1597}
1598
89439d4f 1599int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
e4c691b5
MS
1600 int r;
1601
1602 assert(h);
1603
1604 r = resize_buckets(h, entries_add);
1605 if (r < 0)
1606 return r;
1607
1608 return 0;
1609}
1610
89439d4f
MS
1611/*
1612 * The same as hashmap_merge(), but every new item from other is moved to h.
1613 * Keys already in h are skipped and stay in other.
1614 * Returns: 0 on success.
1615 * -ENOMEM on alloc failure, in which case no move has been done.
1616 */
1617int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
1618 struct swap_entries swap;
1619 struct hashmap_base_entry *e, *n;
1620 Iterator i;
1621 unsigned idx;
1622 int r;
101d8e63
LP
1623
1624 assert(h);
1625
101d8e63 1626 if (!other)
7ad63f57 1627 return 0;
101d8e63 1628
89439d4f
MS
1629 assert(other->type == h->type);
1630
1631 /*
1632 * This reserves buckets for the worst case, where none of other's
1633 * entries are yet present in h. This is preferable to risking
1634 * an allocation failure in the middle of the moving and having to
1635 * rollback or return a partial result.
1636 */
1637 r = resize_buckets(h, n_entries(other));
1638 if (r < 0)
1639 return r;
101d8e63 1640
89439d4f
MS
1641 HASHMAP_FOREACH_IDX(idx, other, i) {
1642 unsigned h_hash;
101d8e63 1643
89439d4f 1644 e = bucket_at(other, idx);
a3b6fafe 1645 h_hash = bucket_hash(h, e->key);
89439d4f 1646 if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
101d8e63
LP
1647 continue;
1648
89439d4f
MS
1649 n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
1650 n->key = e->key;
1651 if (h->type != HASHMAP_TYPE_SET)
1652 ((struct plain_hashmap_entry*) n)->value =
1653 ((struct plain_hashmap_entry*) e)->value;
1654 assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
1655
1656 remove_entry(other, idx);
101d8e63 1657 }
7ad63f57
MS
1658
1659 return 0;
101d8e63
LP
1660}
1661
89439d4f
MS
1662int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
1663 struct swap_entries swap;
1664 unsigned h_hash, other_hash, idx;
1665 struct hashmap_base_entry *e, *n;
1666 int r;
101d8e63 1667
101d8e63
LP
1668 assert(h);
1669
a3b6fafe 1670 h_hash = bucket_hash(h, key);
89439d4f 1671 if (bucket_scan(h, h_hash, key) != IDX_NIL)
101d8e63
LP
1672 return -EEXIST;
1673
bf3d3e2b
MS
1674 if (!other)
1675 return -ENOENT;
1676
89439d4f
MS
1677 assert(other->type == h->type);
1678
a3b6fafe 1679 other_hash = bucket_hash(other, key);
89439d4f
MS
1680 idx = bucket_scan(other, other_hash, key);
1681 if (idx == IDX_NIL)
101d8e63
LP
1682 return -ENOENT;
1683
89439d4f
MS
1684 e = bucket_at(other, idx);
1685
1686 n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
1687 n->key = e->key;
1688 if (h->type != HASHMAP_TYPE_SET)
1689 ((struct plain_hashmap_entry*) n)->value =
1690 ((struct plain_hashmap_entry*) e)->value;
1691 r = hashmap_put_boldly(h, h_hash, &swap, true);
1692 if (r < 0)
1693 return r;
101d8e63 1694
89439d4f 1695 remove_entry(other, idx);
101d8e63
LP
1696 return 0;
1697}
1698
89439d4f
MS
1699HashmapBase *internal_hashmap_copy(HashmapBase *h) {
1700 HashmapBase *copy;
1701 int r;
91cdde8a
LP
1702
1703 assert(h);
1704
89439d4f 1705 copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS);
45fa9e29 1706 if (!copy)
91cdde8a
LP
1707 return NULL;
1708
89439d4f
MS
1709 switch (h->type) {
1710 case HASHMAP_TYPE_PLAIN:
1711 case HASHMAP_TYPE_ORDERED:
1712 r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
1713 break;
1714 case HASHMAP_TYPE_SET:
1715 r = set_merge((Set*)copy, (Set*)h);
1716 break;
1717 default:
1718 assert_not_reached("Unknown hashmap type");
1719 }
1720
1721 if (r < 0) {
1722 internal_hashmap_free(copy);
91cdde8a
LP
1723 return NULL;
1724 }
1725
1726 return copy;
1727}
db1413d7 1728
89439d4f 1729char **internal_hashmap_get_strv(HashmapBase *h) {
db1413d7 1730 char **sv;
89439d4f
MS
1731 Iterator i;
1732 unsigned idx, n;
db1413d7 1733
89439d4f 1734 sv = new(char*, n_entries(h)+1);
729e3769 1735 if (!sv)
db1413d7
KS
1736 return NULL;
1737
1738 n = 0;
89439d4f
MS
1739 HASHMAP_FOREACH_IDX(idx, h, i)
1740 sv[n++] = entry_value(h, bucket_at(h, idx));
db1413d7
KS
1741 sv[n] = NULL;
1742
1743 return sv;
1744}
3c1668da 1745
89439d4f
MS
1746void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
1747 struct ordered_hashmap_entry *e;
1748 unsigned hash, idx;
3c1668da 1749
3c1668da
LP
1750 if (!h)
1751 return NULL;
1752
a3b6fafe 1753 hash = bucket_hash(h, key);
89439d4f
MS
1754 idx = bucket_scan(h, hash, key);
1755 if (idx == IDX_NIL)
3c1668da
LP
1756 return NULL;
1757
89439d4f
MS
1758 e = ordered_bucket_at(h, idx);
1759 if (e->iterate_next == IDX_NIL)
3c1668da 1760 return NULL;
89439d4f
MS
1761 return ordered_bucket_at(h, e->iterate_next)->p.value;
1762}
3c1668da 1763
89439d4f
MS
1764int set_consume(Set *s, void *value) {
1765 int r;
1766
d97c5aea
LP
1767 assert(s);
1768 assert(value);
1769
89439d4f 1770 r = set_put(s, value);
575ccc1b 1771 if (r <= 0)
89439d4f
MS
1772 free(value);
1773
1774 return r;
1775}
1776
1777int set_put_strdup(Set *s, const char *p) {
1778 char *c;
89439d4f
MS
1779
1780 assert(s);
1781 assert(p);
1782
454f0f86
LP
1783 if (set_contains(s, (char*) p))
1784 return 0;
1785
89439d4f
MS
1786 c = strdup(p);
1787 if (!c)
1788 return -ENOMEM;
1789
454f0f86 1790 return set_consume(s, c);
89439d4f
MS
1791}
1792
1793int set_put_strdupv(Set *s, char **l) {
1794 int n = 0, r;
1795 char **i;
1796
d97c5aea
LP
1797 assert(s);
1798
89439d4f
MS
1799 STRV_FOREACH(i, l) {
1800 r = set_put_strdup(s, *i);
1801 if (r < 0)
1802 return r;
1803
1804 n += r;
1805 }
1806
1807 return n;
3c1668da 1808}
d97c5aea
LP
1809
1810int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) {
1811 const char *p = v;
1812 int r;
1813
1814 assert(s);
1815 assert(v);
1816
1817 for (;;) {
1818 char *word;
1819
1820 r = extract_first_word(&p, &word, separators, flags);
1821 if (r <= 0)
1822 return r;
1823
1824 r = set_consume(s, word);
1825 if (r < 0)
1826 return r;
1827 }
1828}