• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 28272947092

26 Jun 2026 08:38PM UTC coverage: 72.893% (+0.2%) from 72.703%
28272947092

push

github

poettering
sysupdate: Address review feedback on CheckNew varlink scaffolding

Follow-up to #42422:

 - Rename process_image() to context_process_image(), since it now
   operates on a Context object.
 - Use IN_SET() in image_type_can_sysupdate() instead of a switch.
 - Name the return parameters of context_list_components() ret_xyz, per
   our coding style.
 - Drop a redundant "else" after a return in vl_method_check_new().

9 of 11 new or added lines in 1 file covered. (81.82%)

12567 existing lines in 144 files now uncovered.

341026 of 467845 relevant lines covered (72.89%)

1339355.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.78
/src/basic/hashmap.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fnmatch.h>
4
#include <pthread.h>
5
#include <unistd.h>
6
#if HAVE_VALGRIND_VALGRIND_H
7
#  include <valgrind/valgrind.h>
8
#endif
9

10
#include "alloc-util.h"
11
#include "extract-word.h"
12
#include "hashmap.h"
13
#include "log.h"
14
#include "logarithm.h"
15
#include "memory-util.h"
16
#include "mempool.h"
17
#include "process-util.h"
18
#include "random-util.h"
19
#include "set.h"
20
#include "siphash24.h"
21
#include "sort-util.h"
22
#include "string-util.h"
23
#include "strv.h"
24

25
#if ENABLE_DEBUG_HASHMAP
26
#include "list.h"
27
#endif
28

29
/*
30
 * Implementation of hashmaps.
31
 * Addressing: open
32
 *   - uses less RAM compared to closed addressing (chaining), because
33
 *     our entries are small (especially in Sets, which tend to contain
34
 *     the majority of entries in systemd).
35
 * Collision resolution: Robin Hood
36
 *   - tends to equalize displacement of entries from their optimal buckets.
37
 * Probe sequence: linear
38
 *   - though theoretically worse than random probing/uniform hashing/double
39
 *     hashing, it is good for cache locality.
40
 *
41
 * References:
42
 * Celis, P. 1986. Robin Hood Hashing.
43
 * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
44
 * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
45
 * - The results are derived for random probing. Suggests deletion with
46
 *   tombstones and two mean-centered search methods. None of that works
47
 *   well for linear probing.
48
 *
49
 * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
50
 * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
51
 * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
52
 * http://www.math.uu.se/~svante/papers/sj157.pdf
53
 * - Applies to Robin Hood with linear probing. Contains remarks on
54
 *   the unsuitability of mean-centered search with linear probing.
55
 *
56
 * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
57
 * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
58
 * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
59
 * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
60
 *   in a successful search), and Janson writes about displacement. C = d + 1.
61
 *
62
 * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
63
 * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
64
 * - Explanation of backward shift deletion with pictures.
65
 *
66
 * Khuong, P. 2013. The Other Robin Hood Hashing.
67
 * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
68
 * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
69
 */
70

71
/*
72
 * XXX Ideas for improvement:
73
 * For unordered hashmaps, randomize iteration order, similarly to Perl:
74
 * http://blog.booking.com/hardening-perls-hash-function.html
75
 */
76

77
/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
78
 * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
79
#define INV_KEEP_FREE            5U
80

81
/* Fields common to entries of all hashmap/set types */
82
struct hashmap_base_entry {
83
        const void *key;
84
};
85

86
/* Entry types for specific hashmap/set types
87
 * hashmap_base_entry must be at the beginning of each entry struct. */
88

89
struct plain_hashmap_entry {
90
        struct hashmap_base_entry b;
91
        void *value;
92
};
93

94
struct ordered_hashmap_entry {
95
        struct plain_hashmap_entry p;
96
        unsigned iterate_next, iterate_previous;
97
};
98

99
struct set_entry {
100
        struct hashmap_base_entry b;
101
};
102

103
/* In several functions it is advantageous to have the hash table extended
104
 * virtually by a couple of additional buckets. We reserve special index values
105
 * for these "swap" buckets. */
106
#define _IDX_SWAP_BEGIN     (UINT_MAX - 3)
107
#define IDX_PUT             (_IDX_SWAP_BEGIN + 0)
108
#define IDX_TMP             (_IDX_SWAP_BEGIN + 1)
109
#define _IDX_SWAP_END       (_IDX_SWAP_BEGIN + 2)
110

111
#define IDX_FIRST           (UINT_MAX - 1) /* special index for freshly initialized iterators */
112
#define IDX_NIL             UINT_MAX       /* special index value meaning "none" or "end" */
113

114
assert_cc(IDX_FIRST == _IDX_SWAP_END);
115
assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
116

117
/* Storage space for the "swap" buckets.
118
 * All entry types can fit into an ordered_hashmap_entry. */
119
struct swap_entries {
120
        struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
121
};
122

123
/* Distance from Initial Bucket */
124
typedef uint8_t dib_raw_t;
125
#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU)   /* indicates DIB value is greater than representable */
126
#define DIB_RAW_REHASH   ((dib_raw_t)0xfeU)   /* entry yet to be rehashed during in-place resize */
127
#define DIB_RAW_FREE     ((dib_raw_t)0xffU)   /* a free bucket */
128
#define DIB_RAW_INIT     ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
129

130
#define DIB_FREE UINT_MAX
131

132
#if ENABLE_DEBUG_HASHMAP
133
struct hashmap_debug_info {
134
        LIST_FIELDS(struct hashmap_debug_info, debug_list);
135
        unsigned max_entries;  /* high watermark of n_entries */
136

137
        /* fields to detect modification while iterating */
138
        unsigned put_count;    /* counts puts into the hashmap */
139
        unsigned rem_count;    /* counts removals from hashmap */
140
        unsigned last_rem_idx; /* remembers last removal index */
141
};
142

143
/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
144
static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
145
static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER;
146
#endif
147

148
enum HashmapType {
149
        HASHMAP_TYPE_PLAIN,
150
        HASHMAP_TYPE_ORDERED,
151
        HASHMAP_TYPE_SET,
152
        _HASHMAP_TYPE_MAX
153
};
154

155
struct _packed_ indirect_storage {
156
        void *storage;                     /* where buckets and DIBs are stored */
157
        uint8_t  hash_key[HASH_KEY_SIZE];  /* hash key; changes during resize */
158

159
        unsigned n_entries;                /* number of stored entries */
160
        unsigned n_buckets;                /* number of buckets */
161

162
        unsigned idx_lowest_entry;         /* Index below which all buckets are free.
163
                                              Makes "while (hashmap_steal_first())" loops
164
                                              O(n) instead of O(n^2) for unordered hashmaps. */
165
        uint8_t  _pad[3];                  /* padding for the whole HashmapBase */
166
        /* The bitfields in HashmapBase complete the alignment of the whole thing. */
167
};
168

169
struct direct_storage {
170
        /* This gives us 39 bytes on 64-bit, or 35 bytes on 32-bit.
171
         * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64-bit,
172
         *              or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32-bit. */
173
        uint8_t storage[sizeof(struct indirect_storage)];
174
};
175

176
#define DIRECT_BUCKETS(entry_t) \
177
        (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
178

179
/* We should be able to store at least one entry directly. */
180
assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
181

182
/* We have 3 bits for n_direct_entries. */
183
assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
184

185
/* Hashmaps with directly stored entries all use this shared hash key.
186
 * It's no big deal if the key is guessed, because there can be only
187
 * a handful of directly stored entries in a hashmap. When a hashmap
188
 * outgrows direct storage, it gets its own key for indirect storage. */
189
static uint8_t shared_hash_key[HASH_KEY_SIZE];
190

191
/* Fields that all hashmap/set types must have */
192
struct HashmapBase {
193
        const struct hash_ops *hash_ops;  /* hash and compare ops to use */
194

195
        union _packed_ {
196
                struct indirect_storage indirect; /* if  has_indirect */
197
                struct direct_storage direct;     /* if !has_indirect */
198
        };
199

200
        enum HashmapType type:2;     /* HASHMAP_TYPE_* */
201
        bool has_indirect:1;         /* whether indirect storage is used */
202
        unsigned n_direct_entries:3; /* Number of entries in direct storage.
203
                                      * Only valid if !has_indirect. */
204
        bool from_pool:1;            /* whether was allocated from mempool */
205
        bool dirty:1;                /* whether dirtied since last iterated_cache_get() */
206
        bool cached:1;               /* whether this hashmap is being cached */
207

208
#if ENABLE_DEBUG_HASHMAP
209
        struct hashmap_debug_info debug;
210
#endif
211
};
212

213
/* Specific hash types
214
 * HashmapBase must be at the beginning of each hashmap struct. */
215

216
struct Hashmap {
217
        struct HashmapBase b;
218
};
219

220
struct OrderedHashmap {
221
        struct HashmapBase b;
222
        unsigned iterate_list_head, iterate_list_tail;
223
};
224

225
struct Set {
226
        struct HashmapBase b;
227
};
228

229
typedef struct CacheMem {
230
        const void **ptr;
231
        size_t n_populated;
232
        bool active:1;
233
} CacheMem;
234

235
struct IteratedCache {
236
        HashmapBase *hashmap;
237
        CacheMem keys, values;
238
};
239

240
DEFINE_MEMPOOL(hashmap_pool,         Hashmap,        8);
241
DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
242
/* No need for a separate Set pool */
243
assert_cc(sizeof(Hashmap) == sizeof(Set));
244

245
struct hashmap_type_info {
246
        size_t head_size;
247
        size_t entry_size;
248
        struct mempool *mempool;
249
        unsigned n_direct_buckets;
250
};
251

252
static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
253
        [HASHMAP_TYPE_PLAIN] = {
254
                .head_size        = sizeof(Hashmap),
255
                .entry_size       = sizeof(struct plain_hashmap_entry),
256
                .mempool          = &hashmap_pool,
257
                .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
258
        },
259
        [HASHMAP_TYPE_ORDERED] = {
260
                .head_size        = sizeof(OrderedHashmap),
261
                .entry_size       = sizeof(struct ordered_hashmap_entry),
262
                .mempool          = &ordered_hashmap_pool,
263
                .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
264
        },
265
        [HASHMAP_TYPE_SET] = {
266
                .head_size        = sizeof(Set),
267
                .entry_size       = sizeof(struct set_entry),
268
                .mempool          = &hashmap_pool,
269
                .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
270
        },
271
};
272

273
void hashmap_trim_pools(void) {
2✔
274
        int r;
2✔
275

276
        /* The pool is only allocated by the main thread, but the memory can be passed to other
277
         * threads. Let's clean up if we are the main thread and no other threads are live. */
278

279
        /* We build our own is_main_thread() here, which doesn't use C11 TLS based caching of the
280
         * result. That's because valgrind apparently doesn't like TLS to be used from a GCC destructor. */
281
        if (getpid() != gettid())
2✔
282
                return (void) log_debug("Not cleaning up memory pools, not in main thread.");
×
283

284
        r = get_process_threads(0);
2✔
285
        if (r < 0)
2✔
286
                return (void) log_debug_errno(r, "Failed to determine number of threads, not cleaning up memory pools: %m");
×
287
        if (r != 1)
2✔
288
                return (void) log_debug("Not cleaning up memory pools, running in multi-threaded process.");
1✔
289

290
        mempool_trim(&hashmap_pool);
1✔
291
        mempool_trim(&ordered_hashmap_pool);
1✔
292
}
293

294
#if HAVE_VALGRIND_VALGRIND_H
295
_destructor_ static void cleanup_pools(void) {
296
        /* Be nice to valgrind */
297
        if (RUNNING_ON_VALGRIND)
298
                hashmap_trim_pools();
299
}
300
#endif
301

302
static unsigned n_buckets(HashmapBase *h) {
2,031,476,428✔
303
        assert(h);
2,031,476,428✔
304
        return h->has_indirect ? h->indirect.n_buckets
2,031,476,428✔
305
                               : hashmap_type_info[h->type].n_direct_buckets;
2,031,476,428✔
306
}
307

308
static unsigned n_entries(HashmapBase *h) {
196,940,292✔
309
        assert(h);
196,940,292✔
310
        return h->has_indirect ? h->indirect.n_entries
196,940,292✔
311
                               : h->n_direct_entries;
196,940,292✔
312
}
313

314
static void n_entries_inc(HashmapBase *h) {
50,231,502✔
315
        assert(h);
50,231,502✔
316

317
        if (h->has_indirect)
50,231,502✔
318
                h->indirect.n_entries++;
41,669,616✔
319
        else
320
                h->n_direct_entries++;
8,561,886✔
321
}
50,231,502✔
322

323
static void n_entries_dec(HashmapBase *h) {
45,574,309✔
324
        assert(h);
45,574,309✔
325

326
        if (h->has_indirect)
45,574,309✔
327
                h->indirect.n_entries--;
39,519,170✔
328
        else
329
                h->n_direct_entries--;
6,055,139✔
330
}
45,574,309✔
331

332
static void* storage_ptr(HashmapBase *h) {
1,862,021,601✔
333
        assert(h);
1,862,021,601✔
334
        return h->has_indirect ? h->indirect.storage
1,862,021,601✔
335
                               : h->direct.storage;
1,862,021,601✔
336
}
337

338
static uint8_t* hash_key(HashmapBase *h) {
315,108,159✔
339
        assert(h);
315,108,159✔
340
        return h->has_indirect ? h->indirect.hash_key
315,108,159✔
341
                               : shared_hash_key;
315,108,159✔
342
}
343

344
static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
315,108,159✔
345
        struct siphash state;
315,108,159✔
346
        uint64_t hash;
315,108,159✔
347

348
        assert(h);
315,108,159✔
349

350
        siphash24_init(&state, hash_key(h));
315,108,159✔
351

352
        h->hash_ops->hash(p, &state);
315,108,159✔
353

354
        hash = siphash24_finalize(&state);
315,108,159✔
355

356
        return (unsigned) (hash % n_buckets(h));
315,108,159✔
357
}
358
#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
359

360
static void base_set_dirty(HashmapBase *h) {
101,438,181✔
361
        assert(h);
101,438,181✔
362

363
        h->dirty = true;
101,438,181✔
364
}
101,438,181✔
365
#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h))
366

367
static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
3,775,005✔
368
        static uint8_t current[HASH_KEY_SIZE];
3,775,005✔
369
        static bool current_initialized = false;
3,775,005✔
370

371
        /* Returns a hash function key to use. In order to keep things
372
         * fast we will not generate a new key each time we allocate a
373
         * new hash table. Instead, we'll just reuse the most recently
374
         * generated one, except if we never generated one or when we
375
         * are rehashing an entire hash table because we reached a
376
         * fill level */
377

378
        if (!current_initialized || !reuse_is_ok) {
3,775,005✔
379
                random_bytes(current, sizeof(current));
2,299,662✔
380
                current_initialized = true;
2,299,662✔
381
        }
382

383
        memcpy(hash_key, current, sizeof(current));
3,775,005✔
384
}
3,775,005✔
385

386
static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) {
1,145,819,411✔
387
        assert(h);
1,145,819,411✔
388
        return CAST_ALIGN_PTR(
1,145,819,411✔
389
                        struct hashmap_base_entry,
390
                        (uint8_t *) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
391
}
392

393
static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) {
2,631,871✔
394
        return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
2,631,871✔
395
}
396

397
static struct ordered_hashmap_entry* ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
96,426,229✔
398
        return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
96,426,229✔
399
}
400

401
static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
4✔
402
        return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
4✔
403
}
404

405
static struct ordered_hashmap_entry* bucket_at_swap(struct swap_entries *swap, unsigned idx) {
456,102,840✔
406
        assert(swap);
456,102,840✔
407
        return &swap->e[idx - _IDX_SWAP_BEGIN];
456,102,840✔
408
}
409

410
/* Returns a pointer to the bucket at index idx.
411
 * Understands real indexes and swap indexes, hence "_virtual". */
412
static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
749,241,324✔
413
                                                    unsigned idx) {
414
        if (idx < _IDX_SWAP_BEGIN)
749,241,324✔
415
                return bucket_at(h, idx);
404,830,116✔
416

417
        if (idx < _IDX_SWAP_END)
344,411,208✔
418
                return &bucket_at_swap(swap, idx)->p.b;
344,411,208✔
419

UNCOV
420
        assert_not_reached();
×
421
}
422

423
static dib_raw_t* dib_raw_ptr(HashmapBase *h) {
716,202,190✔
424
        assert(h);
716,202,190✔
425
        return (dib_raw_t*)
1,432,404,380✔
426
                ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
716,202,190✔
427
}
428

UNCOV
429
static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
×
UNCOV
430
        return idx >= from ? idx - from
×
UNCOV
431
                           : n_buckets(h) + idx - from;
×
432
}
433

434
static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
442,150,856✔
435
        unsigned initial_bucket;
442,150,856✔
436

437
        if (raw_dib == DIB_RAW_FREE)
442,150,856✔
438
                return DIB_FREE;
439

440
        if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
442,150,856✔
441
                return raw_dib;
442,150,856✔
442

443
        /*
444
         * Having an overflow DIB value is very unlikely. The hash function
445
         * would have to be bad. For example, in a table of size 2^24 filled
446
         * to load factor 0.9 the maximum observed DIB is only about 60.
447
         * In theory (assuming I used Maxima correctly), for an infinite size
448
         * hash table with load factor 0.8 the probability of a given entry
449
         * having DIB > 40 is 1.9e-8.
450
         * This returns the correct DIB value by recomputing the hash value in
451
         * the unlikely case. XXX Hitting this case could be a hint to rehash.
452
         */
UNCOV
453
        initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
×
UNCOV
454
        return bucket_distance(h, idx, initial_bucket);
×
455
}
456

457
static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
207,811,573✔
458
        dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
207,811,573✔
459
}
207,811,573✔
460

461
static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
98,544,155✔
462
        dib_raw_t *dibs;
98,544,155✔
463

464
        dibs = dib_raw_ptr(h);
98,544,155✔
465

466
        for ( ; idx < n_buckets(h); idx++)
301,264,647✔
467
                if (dibs[idx] != DIB_RAW_FREE)
182,809,741✔
468
                        return idx;
469

470
        return IDX_NIL;
471
}
472

473
static void bucket_mark_free(HashmapBase *h, unsigned idx) {
45,574,309✔
474
        assert(h);
45,574,309✔
475

476
        memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size);
45,574,309✔
477
        bucket_set_dib(h, idx, DIB_FREE);
45,574,309✔
478
}
45,574,309✔
479

480
static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
307,748,500✔
481
                              unsigned from, unsigned to) {
482
        struct hashmap_base_entry *e_from, *e_to;
307,748,500✔
483

484
        assert(h);
307,748,500✔
485
        assert(from != to);
307,748,500✔
486

487
        e_from = bucket_at_virtual(h, swap, from);
307,748,500✔
488
        e_to   = bucket_at_virtual(h, swap, to);
307,748,500✔
489

490
        memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
307,748,500✔
491

492
        if (h->type == HASHMAP_TYPE_ORDERED) {
307,748,500✔
493
                OrderedHashmap *lh = (OrderedHashmap*) h;
83,024,612✔
494
                struct ordered_hashmap_entry *le, *le_to;
83,024,612✔
495

496
                le_to = (struct ordered_hashmap_entry*) e_to;
83,024,612✔
497

498
                if (le_to->iterate_next != IDX_NIL) {
83,024,612✔
499
                        le = (struct ordered_hashmap_entry*)
59,748,415✔
500
                             bucket_at_virtual(h, swap, le_to->iterate_next);
59,748,415✔
501
                        le->iterate_previous = to;
59,748,415✔
502
                }
503

504
                if (le_to->iterate_previous != IDX_NIL) {
83,024,612✔
505
                        le = (struct ordered_hashmap_entry*)
73,995,909✔
506
                             bucket_at_virtual(h, swap, le_to->iterate_previous);
73,995,909✔
507
                        le->iterate_next = to;
73,995,909✔
508
                }
509

510
                if (lh->iterate_list_head == from)
83,024,612✔
511
                        lh->iterate_list_head = to;
9,028,703✔
512
                if (lh->iterate_list_tail == from)
83,024,612✔
513
                        lh->iterate_list_tail = to;
23,276,197✔
514
        }
515
}
307,748,500✔
516

517
static unsigned next_idx(HashmapBase *h, unsigned idx) {
389,318,026✔
518
        return (idx + 1U) % n_buckets(h);
389,318,026✔
519
}
520

521
static unsigned prev_idx(HashmapBase *h, unsigned idx) {
91✔
522
        return (n_buckets(h) + idx - 1U) % n_buckets(h);
91✔
523
}
524

525
static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
207,025,926✔
526
        assert(h);
207,025,926✔
527
        assert(e);
207,025,926✔
528

529
        switch (h->type) {
207,025,926✔
530

531
        case HASHMAP_TYPE_PLAIN:
185,190,206✔
532
        case HASHMAP_TYPE_ORDERED:
533
                return ((struct plain_hashmap_entry*)e)->value;
185,190,206✔
534

535
        case HASHMAP_TYPE_SET:
21,835,720✔
536
                return (void*) e->key;
21,835,720✔
537

UNCOV
538
        default:
×
UNCOV
539
                assert_not_reached();
×
540
        }
541
}
542

543
static void base_remove_entry(HashmapBase *h, unsigned idx) {
45,574,309✔
544
        unsigned left, right, prev, dib;
45,574,309✔
545
        dib_raw_t raw_dib, *dibs;
45,574,309✔
546

547
        dibs = dib_raw_ptr(h);
45,574,309✔
548
        assert(dibs[idx] != DIB_RAW_FREE);
45,574,309✔
549

550
#if ENABLE_DEBUG_HASHMAP
551
        assert(h);
552
        h->debug.rem_count++;
553
        h->debug.last_rem_idx = idx;
554
#endif
555

556
        left = idx;
45,574,309✔
557
        /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
558
        for (right = next_idx(h, left); ; right = next_idx(h, right)) {
45,574,309✔
559
                raw_dib = dibs[right];
64,523,811✔
560
                if (IN_SET(raw_dib, 0, DIB_RAW_FREE))
64,523,811✔
561
                        break;
562

563
                /* The buckets are not supposed to be all occupied and with DIB > 0.
564
                 * That would mean we could make everyone better off by shifting them
565
                 * backward. This scenario is impossible. */
566
                assert(left != right);
18,949,502✔
567
        }
568

569
        if (h->type == HASHMAP_TYPE_ORDERED) {
45,574,309✔
570
                OrderedHashmap *lh = (OrderedHashmap*) h;
17,238,243✔
571
                struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
17,238,243✔
572

573
                if (le->iterate_next != IDX_NIL)
17,238,243✔
574
                        ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
15,814,304✔
575
                else
576
                        lh->iterate_list_tail = le->iterate_previous;
1,423,939✔
577

578
                if (le->iterate_previous != IDX_NIL)
17,238,243✔
579
                        ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
13,810✔
580
                else
581
                        lh->iterate_list_head = le->iterate_next;
17,224,433✔
582
        }
583

584
        /* Now shift all buckets in the interval (left, right) one step backwards */
585
        for (prev = left, left = next_idx(h, left); left != right;
64,523,811✔
586
             prev = left, left = next_idx(h, left)) {
18,949,502✔
587
                dib = bucket_calculate_dib(h, left, dibs[left]);
18,949,502✔
588
                assert(dib != 0);
18,949,502✔
589
                bucket_move_entry(h, NULL, left, prev);
18,949,502✔
590
                bucket_set_dib(h, prev, dib - 1);
18,949,502✔
591
        }
592

593
        bucket_mark_free(h, prev);
45,574,309✔
594
        n_entries_dec(h);
45,574,309✔
595
        base_set_dirty(h);
45,574,309✔
596
}
45,574,309✔
597
#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
598

599
static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
26,277,247✔
600
        struct ordered_hashmap_entry *e;
26,277,247✔
601
        unsigned idx;
26,277,247✔
602

603
        assert(h);
26,277,247✔
604
        assert(i);
26,277,247✔
605

606
        if (i->idx == IDX_NIL)
26,277,247✔
607
                goto at_end;
738,788✔
608

609
        if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
25,538,459✔
610
                goto at_end;
842,108✔
611

612
        if (i->idx == IDX_FIRST) {
24,696,351✔
613
                idx = h->iterate_list_head;
17,950,442✔
614
                e = ordered_bucket_at(h, idx);
17,950,442✔
615
        } else {
616
                idx = i->idx;
6,745,909✔
617
                e = ordered_bucket_at(h, idx);
6,745,909✔
618
                /*
619
                 * We allow removing the current entry while iterating, but removal may cause
620
                 * a backward shift. The next entry may thus move one bucket to the left.
621
                 * To detect when it happens, we remember the key pointer of the entry we were
622
                 * going to iterate next. If it does not match, there was a backward shift.
623
                 */
624
                if (e->p.b.key != i->next_key) {
6,745,909✔
625
                        idx = prev_idx(HASHMAP_BASE(h), idx);
83✔
626
                        e = ordered_bucket_at(h, idx);
83✔
627
                }
628
                assert(e->p.b.key == i->next_key);
6,745,909✔
629
        }
630

631
#if ENABLE_DEBUG_HASHMAP
632
        i->prev_idx = idx;
633
#endif
634

635
        if (e->iterate_next != IDX_NIL) {
24,696,351✔
636
                struct ordered_hashmap_entry *n;
22,570,990✔
637
                i->idx = e->iterate_next;
22,570,990✔
638
                n = ordered_bucket_at(h, i->idx);
22,570,990✔
639
                i->next_key = n->p.b.key;
22,570,990✔
640
        } else
641
                i->idx = IDX_NIL;
2,125,361✔
642

643
        return idx;
644

645
at_end:
1,580,896✔
646
        i->idx = IDX_NIL;
1,580,896✔
647
        return IDX_NIL;
1,580,896✔
648
}
649

650
static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
76,930,368✔
651
        unsigned idx;
76,930,368✔
652

653
        assert(h);
76,930,368✔
654
        assert(i);
76,930,368✔
655

656
        if (i->idx == IDX_NIL)
76,930,368✔
657
                goto at_end;
4,948,669✔
658

659
        if (i->idx == IDX_FIRST) {
71,981,699✔
660
                /* fast forward to the first occupied bucket */
661
                if (h->has_indirect) {
28,372,466✔
662
                        i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
10,401,793✔
663
                        h->indirect.idx_lowest_entry = i->idx;
10,401,793✔
664
                } else
665
                        i->idx = skip_free_buckets(h, 0);
17,970,673✔
666

667
                if (i->idx == IDX_NIL)
28,372,466✔
668
                        goto at_end;
1,810,010✔
669
        } else {
670
                struct hashmap_base_entry *e;
43,609,233✔
671

672
                assert(i->idx > 0);
43,609,233✔
673

674
                e = bucket_at(h, i->idx);
43,609,233✔
675
                /*
676
                 * We allow removing the current entry while iterating, but removal may cause
677
                 * a backward shift. The next entry may thus move one bucket to the left.
678
                 * To detect when it happens, we remember the key pointer of the entry we were
679
                 * going to iterate next. If it does not match, there was a backward shift.
680
                 */
681
                if (e->key != i->next_key)
43,609,233✔
682
                        e = bucket_at(h, --i->idx);
32,096✔
683

684
                assert(e->key == i->next_key);
43,609,233✔
685
        }
686

687
        idx = i->idx;
70,171,689✔
688
#if ENABLE_DEBUG_HASHMAP
689
        i->prev_idx = idx;
690
#endif
691

692
        i->idx = skip_free_buckets(h, i->idx + 1);
70,171,689✔
693
        if (i->idx != IDX_NIL)
70,171,689✔
694
                i->next_key = bucket_at(h, i->idx)->key;
52,070,948✔
695
        else
696
                i->idx = IDX_NIL;
18,100,741✔
697

698
        return idx;
699

700
at_end:
6,758,679✔
701
        i->idx = IDX_NIL;
6,758,679✔
702
        return IDX_NIL;
6,758,679✔
703
}
704

705
static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
122,462,369✔
706
        if (!h) {
122,462,369✔
707
                i->idx = IDX_NIL;
19,254,754✔
708
                return IDX_NIL;
19,254,754✔
709
        }
710

711
#if ENABLE_DEBUG_HASHMAP
712
        if (i->idx == IDX_FIRST) {
713
                i->put_count = h->debug.put_count;
714
                i->rem_count = h->debug.rem_count;
715
        } else {
716
                /* While iterating, must not add any new entries */
717
                assert(i->put_count == h->debug.put_count);
718
                /* ... or remove entries other than the current one */
719
                assert(i->rem_count == h->debug.rem_count ||
720
                       (i->rem_count == h->debug.rem_count - 1 &&
721
                        i->prev_idx == h->debug.last_rem_idx));
722
                /* Reset our removals counter */
723
                i->rem_count = h->debug.rem_count;
724
        }
725
#endif
726

727
        return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
26,277,247✔
728
                                               : hashmap_iterate_in_internal_order(h, i);
103,207,615✔
729
}
730

731
bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
93,798,438✔
732
        struct hashmap_base_entry *e;
93,798,438✔
733
        void *data;
93,798,438✔
734
        unsigned idx;
93,798,438✔
735

736
        idx = hashmap_iterate_entry(h, i);
93,798,438✔
737
        if (idx == IDX_NIL) {
93,798,438✔
738
                if (value)
27,529,125✔
739
                        *value = NULL;
25,141,222✔
740
                if (key)
27,529,125✔
741
                        *key = NULL;
8,095,716✔
742

743
                return false;
744
        }
745

746
        e = bucket_at(h, idx);
66,269,313✔
747
        data = entry_value(h, e);
66,269,313✔
748
        if (value)
66,269,313✔
749
                *value = data;
45,157,706✔
750
        if (key)
66,269,313✔
751
                *key = e->key;
38,364,250✔
752

753
        return true;
754
}
755

756
#define HASHMAP_FOREACH_IDX(idx, h, i) \
757
        for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
758
             (idx != IDX_NIL); \
759
             (idx) = hashmap_iterate_entry((h), &(i)))
760

761
IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h) {
7,504✔
762
        IteratedCache *cache;
7,504✔
763

764
        assert(h);
7,504✔
765
        assert(!h->cached);
7,504✔
766

767
        if (h->cached)
7,504✔
768
                return NULL;
769

770
        cache = new0(IteratedCache, 1);
7,504✔
771
        if (!cache)
7,504✔
772
                return NULL;
773

774
        cache->hashmap = h;
7,504✔
775
        h->cached = true;
7,504✔
776

777
        return cache;
7,504✔
778
}
779

780
static void reset_direct_storage(HashmapBase *h) {
8,880,529✔
781
        const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
8,880,529✔
782
        void *p;
8,880,529✔
783

784
        assert(!h->has_indirect);
8,880,529✔
785

786
        p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
8,880,529✔
787
        memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
8,880,529✔
788
}
8,880,529✔
789

790
static void shared_hash_key_initialize(void) {
72,662✔
791
        random_bytes(shared_hash_key, sizeof(shared_hash_key));
72,662✔
792
}
72,662✔
793

794
static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type) {
4,453,235✔
795
        HashmapBase *h;
4,453,235✔
796
        const struct hashmap_type_info *hi = &hashmap_type_info[type];
4,453,235✔
797

798
        bool use_pool = mempool_enabled && mempool_enabled();  /* mempool_enabled is a weak symbol */
4,453,235✔
799

800
        h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
4,538,547✔
801
        if (!h)
4,453,235✔
802
                return NULL;
803

804
        h->type = type;
4,453,235✔
805
        h->from_pool = use_pool;
4,453,235✔
806
        h->hash_ops = hash_ops ?: &trivial_hash_ops;
4,453,235✔
807

808
        if (type == HASHMAP_TYPE_ORDERED) {
4,453,235✔
809
                OrderedHashmap *lh = (OrderedHashmap*)h;
1,163,726✔
810
                lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
1,163,726✔
811
        }
812

813
        reset_direct_storage(h);
4,453,235✔
814

815
        static pthread_once_t once = PTHREAD_ONCE_INIT;
4,453,235✔
816
        assert_se(pthread_once(&once, shared_hash_key_initialize) == 0);
4,453,235✔
817

818
#if ENABLE_DEBUG_HASHMAP
819
        assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
820
        LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
821
        assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
822
#endif
823

824
        return h;
825
}
826

827
Hashmap *hashmap_new(const struct hash_ops *hash_ops) {
899,930✔
828
        return (Hashmap*)        hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN);
899,930✔
829
}
830

831
OrderedHashmap *ordered_hashmap_new(const struct hash_ops *hash_ops) {
54,022✔
832
        return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED);
54,022✔
833
}
834

835
Set *set_new(const struct hash_ops *hash_ops) {
144,518✔
836
        return (Set*)            hashmap_base_new(hash_ops, HASHMAP_TYPE_SET);
144,518✔
837
}
838

839
static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
44,050,916✔
840
                                         enum HashmapType type) {
841
        HashmapBase *q;
44,050,916✔
842

843
        assert(h);
44,050,916✔
844

845
        if (*h) {
44,050,916✔
846
                assert((*h)->hash_ops == (hash_ops ?: &trivial_hash_ops));
43,982,971✔
847
                return 0;
848
        }
849

850
        q = hashmap_base_new(hash_ops, type);
3,354,762✔
851
        if (!q)
3,354,762✔
852
                return -ENOMEM;
853

854
        *h = q;
3,354,762✔
855
        return 1;
3,354,762✔
856
}
857

858
int hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops) {
7,731,867✔
859
        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN);
7,731,867✔
860
}
861

862
int ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops) {
18,195,302✔
863
        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED);
18,195,302✔
864
}
865

866
int set_ensure_allocated(Set **s, const struct hash_ops *hash_ops) {
18,123,747✔
867
        return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET);
18,123,747✔
868
}
869

870
int hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) {
6,284,866✔
871
        int r;
6,284,866✔
872

873
        assert(h);
6,284,866✔
874

875
        r = hashmap_ensure_allocated(h, hash_ops);
6,284,866✔
876
        if (r < 0)
6,284,866✔
877
                return r;
878

879
        return hashmap_put(*h, key, value);
6,284,866✔
880
}
881

882
int ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) {
5,171,980✔
883
        int r;
5,171,980✔
884

885
        assert(h);
5,171,980✔
886

887
        r = ordered_hashmap_ensure_allocated(h, hash_ops);
5,171,980✔
888
        if (r < 0)
5,171,980✔
889
                return r;
890

891
        return ordered_hashmap_put(*h, key, value);
5,171,980✔
892
}
893

894
int ordered_hashmap_ensure_replace(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) {
6,172✔
895
        int r;
6,172✔
896

897
        assert(h);
6,172✔
898

899
        r = ordered_hashmap_ensure_allocated(h, hash_ops);
6,172✔
900
        if (r < 0)
6,172✔
901
                return r;
902

903
        return ordered_hashmap_replace(*h, key, value);
6,172✔
904
}
905

906
int hashmap_ensure_replace(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) {
26,988✔
907
        int r;
26,988✔
908

909
        assert(h);
26,988✔
910

911
        r = hashmap_ensure_allocated(h, hash_ops);
26,988✔
912
        if (r < 0)
26,988✔
913
                return r;
914

915
        return hashmap_replace(*h, key, value);
26,988✔
916
}
917

918
static void hashmap_free_no_clear(HashmapBase *h) {
4,404,165✔
919
        assert(!h->has_indirect);
4,404,165✔
920
        assert(h->n_direct_entries == 0);
4,404,165✔
921

922
#if ENABLE_DEBUG_HASHMAP
923
        assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
924
        LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
925
        assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
926
#endif
927

928
        if (h->from_pool) {
4,404,165✔
929
                /* Ensure that the object didn't get migrated between threads. */
930
                assert_se(is_main_thread());
4,319,499✔
931
                mempool_free_tile(hashmap_type_info[h->type].mempool, h);
4,319,499✔
932
        } else
933
                free(h);
84,666✔
934
}
4,404,165✔
935

936
HashmapBase* _hashmap_free(HashmapBase *h) {
17,029,045✔
937
        if (h) {
17,029,045✔
938
                _hashmap_clear(h);
4,404,165✔
939
                hashmap_free_no_clear(h);
4,404,165✔
940
        }
941

942
        return NULL;
17,029,045✔
943
}
944

945
void _hashmap_clear(HashmapBase *h) {
4,581,896✔
946
        if (!h)
4,581,896✔
947
                return;
948

949
        if (h->hash_ops->free_key || h->hash_ops->free_value) {
4,427,294✔
950

951
                /* If destructor calls are defined, let's destroy things defensively: let's take the item out of the
952
                 * hash table, and only then call the destructor functions. If these destructors then try to unregister
953
                 * themselves from our hash table a second time, the entry is already gone. */
954

955
                while (_hashmap_size(h) > 0) {
23,393,505✔
956
                        void *k = NULL;
20,261,715✔
957
                        void *v;
20,261,715✔
958

959
                        v = _hashmap_first_key_and_value(h, true, &k);
20,261,715✔
960

961
                        if (h->hash_ops->free_key)
20,261,715✔
962
                                h->hash_ops->free_key(k);
15,701,934✔
963

964
                        if (h->hash_ops->free_value)
20,261,715✔
965
                                h->hash_ops->free_value(v);
17,716,024✔
966
                }
967
        }
968

969
        if (h->has_indirect) {
4,427,294✔
970
                free(h->indirect.storage);
1,499,392✔
971
                h->has_indirect = false;
1,499,392✔
972
        }
973

974
        h->n_direct_entries = 0;
4,427,294✔
975
        reset_direct_storage(h);
4,427,294✔
976

977
        if (h->type == HASHMAP_TYPE_ORDERED) {
4,427,294✔
978
                OrderedHashmap *lh = (OrderedHashmap*) h;
1,159,445✔
979
                lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
1,159,445✔
980
        }
981

982
        base_set_dirty(h);
4,427,294✔
983
}
984

985
static int resize_buckets(HashmapBase *h, unsigned entries_add);
986

987
/*
988
 * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
989
 * Performs Robin Hood swaps as it goes. The entry to put must be placed
990
 * by the caller into swap slot IDX_PUT.
991
 * If used for in-place resizing, may leave a displaced entry in swap slot
992
 * IDX_PUT. Caller must rehash it next.
993
 * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
994
 *          false otherwise.
995
 */
996
static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
102,061,414✔
997
                                   struct swap_entries *swap) {
998
        dib_raw_t raw_dib, *dibs;
102,061,414✔
999
        unsigned dib, distance;
102,061,414✔
1000

1001
#if ENABLE_DEBUG_HASHMAP
1002
        assert(h);
1003
        h->debug.put_count++;
1004
#endif
1005

1006
        dibs = dib_raw_ptr(h);
102,061,414✔
1007

1008
        for (distance = 0; ; distance++) {
102,061,414✔
1009
                raw_dib = dibs[idx];
205,081,657✔
1010
                if (IN_SET(raw_dib, DIB_RAW_FREE, DIB_RAW_REHASH)) {
205,081,657✔
1011
                        if (raw_dib == DIB_RAW_REHASH)
102,061,414✔
1012
                                bucket_move_entry(h, swap, idx, IDX_TMP);
11,228,628✔
1013

1014
                        if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
102,061,414✔
1015
                                h->indirect.idx_lowest_entry = idx;
36,584✔
1016

1017
                        bucket_set_dib(h, idx, distance);
102,061,414✔
1018
                        bucket_move_entry(h, swap, IDX_PUT, idx);
102,061,414✔
1019
                        if (raw_dib == DIB_RAW_REHASH) {
102,061,414✔
1020
                                bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
11,228,628✔
1021
                                return true;
11,228,628✔
1022
                        }
1023

1024
                        return false;
1025
                }
1026

1027
                dib = bucket_calculate_dib(h, idx, raw_dib);
103,020,243✔
1028

1029
                if (dib < distance) {
103,020,243✔
1030
                        /* Found a wealthier entry. Go Robin Hood! */
1031
                        bucket_set_dib(h, idx, distance);
41,226,348✔
1032

1033
                        /* swap the entries */
1034
                        bucket_move_entry(h, swap, idx, IDX_TMP);
41,226,348✔
1035
                        bucket_move_entry(h, swap, IDX_PUT, idx);
41,226,348✔
1036
                        bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
41,226,348✔
1037

1038
                        distance = dib;
41,226,348✔
1039
                }
1040

1041
                idx = next_idx(h, idx);
103,020,243✔
1042
        }
1043
}
1044

1045
/*
1046
 * Puts an entry into a hashmap, boldly - no check whether key already exists.
1047
 * The caller must place the entry (only its key and value, not link indexes)
1048
 * in swap slot IDX_PUT.
1049
 * Caller must ensure: the key does not exist yet in the hashmap.
1050
 *                     that resize is not needed if !may_resize.
1051
 * Returns: 1 if entry was put successfully.
1052
 *          -ENOMEM if may_resize==true and resize failed with -ENOMEM.
1053
 *          Cannot return -ENOMEM if !may_resize.
1054
 */
1055
static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
50,231,502✔
1056
                                   struct swap_entries *swap, bool may_resize) {
1057
        struct ordered_hashmap_entry *new_entry;
50,231,502✔
1058
        int r;
50,231,502✔
1059

1060
        assert(idx < n_buckets(h));
50,231,502✔
1061

1062
        new_entry = bucket_at_swap(swap, IDX_PUT);
50,231,502✔
1063

1064
        if (may_resize) {
50,231,502✔
1065
                r = resize_buckets(h, 1);
50,226,798✔
1066
                if (r < 0)
50,226,798✔
1067
                        return r;
1068
                if (r > 0)
50,226,798✔
1069
                        idx = bucket_hash(h, new_entry->p.b.key);
3,771,534✔
1070
        }
1071
        assert(n_entries(h) < n_buckets(h));
50,231,502✔
1072

1073
        if (h->type == HASHMAP_TYPE_ORDERED) {
50,231,502✔
1074
                OrderedHashmap *lh = (OrderedHashmap*) h;
17,526,865✔
1075

1076
                new_entry->iterate_next = IDX_NIL;
17,526,865✔
1077
                new_entry->iterate_previous = lh->iterate_list_tail;
17,526,865✔
1078

1079
                if (lh->iterate_list_tail != IDX_NIL) {
17,526,865✔
1080
                        struct ordered_hashmap_entry *old_tail;
16,091,751✔
1081

1082
                        old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
16,091,751✔
1083
                        assert(old_tail->iterate_next == IDX_NIL);
16,091,751✔
1084
                        old_tail->iterate_next = IDX_PUT;
16,091,751✔
1085
                }
1086

1087
                lh->iterate_list_tail = IDX_PUT;
17,526,865✔
1088
                if (lh->iterate_list_head == IDX_NIL)
17,526,865✔
1089
                        lh->iterate_list_head = IDX_PUT;
1,435,114✔
1090
        }
1091

1092
        assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
50,231,502✔
1093

1094
        n_entries_inc(h);
50,231,502✔
1095
#if ENABLE_DEBUG_HASHMAP
1096
        h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
1097
#endif
1098

1099
        base_set_dirty(h);
50,231,502✔
1100

1101
        return 1;
50,231,502✔
1102
}
1103
#define hashmap_put_boldly(h, idx, swap, may_resize) \
1104
        hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
1105

1106
/*
1107
 * Returns 0 if resize is not needed.
1108
 *         1 if successfully resized.
1109
 *         -ENOMEM on allocation failure.
1110
 */
1111
static int resize_buckets(HashmapBase *h, unsigned entries_add) {
50,239,661✔
1112
        struct swap_entries swap;
50,239,661✔
1113
        void *new_storage;
50,239,661✔
1114
        dib_raw_t *old_dibs, *new_dibs;
50,239,661✔
1115
        const struct hashmap_type_info *hi;
50,239,661✔
1116
        unsigned idx, optimal_idx;
50,239,661✔
1117
        unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
50,239,661✔
1118
        uint8_t new_shift;
50,239,661✔
1119
        bool rehash_next;
50,239,661✔
1120

1121
        assert(h);
50,239,661✔
1122

1123
        hi = &hashmap_type_info[h->type];
50,239,661✔
1124
        new_n_entries = n_entries(h) + entries_add;
50,239,661✔
1125

1126
        /* overflow? */
1127
        if (_unlikely_(new_n_entries < entries_add))
50,239,661✔
1128
                return -ENOMEM;
50,239,661✔
1129

1130
        /* For direct storage we allow 100% load, because it's tiny. */
1131
        if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
50,239,659✔
1132
                return 0;
1133

1134
        /*
1135
         * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
1136
         * From it follows: m = n + n/(INV_KEEP_FREE - 1)
1137
         */
1138
        new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
41,677,986✔
1139
        /* overflow? */
1140
        if (_unlikely_(new_n_buckets < new_n_entries))
41,677,986✔
1141
                return -ENOMEM;
1142

1143
        if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
41,677,984✔
1144
                return -ENOMEM;
1145

1146
        old_n_buckets = n_buckets(h);
41,677,984✔
1147

1148
        if (_likely_(new_n_buckets <= old_n_buckets))
41,677,984✔
1149
                return 0;
1150

1151
        new_shift = log2u_round_up(MAX(
3,775,005✔
1152
                        new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
1153
                        2 * sizeof(struct direct_storage)));
1154

1155
        /* Realloc storage (buckets and DIB array). */
1156
        new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
2,257,639✔
1157
                              1U << new_shift);
3,775,005✔
1158
        if (!new_storage)
3,775,005✔
1159
                return -ENOMEM;
1160

1161
        /* Must upgrade direct to indirect storage. */
1162
        if (!h->has_indirect) {
3,775,005✔
1163
                memcpy(new_storage, h->direct.storage,
1,517,366✔
1164
                       old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
1,517,366✔
1165
                h->indirect.n_entries = h->n_direct_entries;
1,517,366✔
1166
                h->indirect.idx_lowest_entry = 0;
1,517,366✔
1167
                h->n_direct_entries = 0;
1,517,366✔
1168
        }
1169

1170
        /* Get a new hash key. If we've just upgraded to indirect storage,
1171
         * allow reusing a previously generated key. It's still a different key
1172
         * from the shared one that we used for direct storage. */
1173
        get_hash_key(h->indirect.hash_key, !h->has_indirect);
3,775,005✔
1174

1175
        h->has_indirect = true;
3,775,005✔
1176
        h->indirect.storage = new_storage;
3,775,005✔
1177
        h->indirect.n_buckets = (1U << new_shift) /
3,775,005✔
1178
                                (hi->entry_size + sizeof(dib_raw_t));
1179

1180
        old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets);
3,775,005✔
1181
        new_dibs = dib_raw_ptr(h);
3,775,005✔
1182

1183
        /*
1184
         * Move the DIB array to the new place, replacing valid DIB values with
1185
         * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
1186
         * Note: Overlap is not possible, because we have at least doubled the
1187
         * number of buckets and dib_raw_t is smaller than any entry type.
1188
         */
1189
        for (idx = 0; idx < old_n_buckets; idx++) {
69,178,489✔
1190
                assert(old_dibs[idx] != DIB_RAW_REHASH);
65,403,484✔
1191
                new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
118,304,375✔
1192
                                                              : DIB_RAW_REHASH;
1193
        }
1194

1195
        /* Zero the area of newly added entries (including the old DIB area) */
1196
        memzero(bucket_at(h, old_n_buckets),
3,775,005✔
1197
               (n_buckets(h) - old_n_buckets) * hi->entry_size);
1198

1199
        /* The upper half of the new DIB array needs initialization */
1200
        memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
7,550,010✔
1201
               (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
3,775,005✔
1202

1203
        /* Rehash entries that need it */
1204
        n_rehashed = 0;
3,775,005✔
1205
        for (idx = 0; idx < old_n_buckets; idx++) {
69,178,489✔
1206
                if (new_dibs[idx] != DIB_RAW_REHASH)
65,403,484✔
1207
                        continue;
23,731,221✔
1208

1209
                optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
41,672,263✔
1210

1211
                /*
1212
                 * Not much to do if by luck the entry hashes to its current
1213
                 * location. Just set its DIB.
1214
                 */
1215
                if (optimal_idx == idx) {
41,672,263✔
1216
                        new_dibs[idx] = 0;
1,070,979✔
1217
                        n_rehashed++;
1,070,979✔
1218
                        continue;
1,070,979✔
1219
                }
1220

1221
                new_dibs[idx] = DIB_RAW_FREE;
40,601,284✔
1222
                bucket_move_entry(h, &swap, idx, IDX_PUT);
40,601,284✔
1223
                /* bucket_move_entry does not clear the source */
1224
                memzero(bucket_at(h, idx), hi->entry_size);
40,601,284✔
1225

1226
                do {
51,829,912✔
1227
                        /*
1228
                         * Find the new bucket for the current entry. This may make
1229
                         * another entry homeless and load it into IDX_PUT.
1230
                         */
1231
                        rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
51,829,912✔
1232
                        n_rehashed++;
51,829,912✔
1233

1234
                        /* Did the current entry displace another one? */
1235
                        if (rehash_next)
51,829,912✔
1236
                                optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
11,228,628✔
1237
                } while (rehash_next);
51,829,912✔
1238
        }
1239

1240
        assert_se(n_rehashed == n_entries(h));
3,775,005✔
1241

1242
        return 1;
1243
}
1244

1245
/*
1246
 * Finds an entry with a matching key
1247
 * Returns: index of the found entry, or IDX_NIL if not found.
1248
 */
1249
static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
258,435,734✔
1250
        struct hashmap_base_entry *e;
258,435,734✔
1251
        unsigned dib, distance;
258,435,734✔
1252
        dib_raw_t *dibs = dib_raw_ptr(h);
258,435,734✔
1253

1254
        assert(idx < n_buckets(h));
258,435,734✔
1255

1256
        for (distance = 0; ; distance++) {
157,250,161✔
1257
                if (dibs[idx] == DIB_RAW_FREE)
415,685,895✔
1258
                        return IDX_NIL;
1259

1260
                dib = bucket_calculate_dib(h, idx, dibs[idx]);
320,181,111✔
1261

1262
                if (dib < distance)
320,181,111✔
1263
                        return IDX_NIL;
1264
                if (dib == distance) {
273,747,611✔
1265
                        e = bucket_at(h, idx);
207,558,715✔
1266
                        if (h->hash_ops->compare(e->key, key) == 0)
207,558,715✔
1267
                                return idx;
1268
                }
1269

1270
                idx = next_idx(h, idx);
157,250,161✔
1271
        }
1272
}
1273
#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
1274

1275
int hashmap_put(Hashmap *h, const void *key, void *value) {
17,364,207✔
1276
        struct swap_entries swap;
17,364,207✔
1277
        struct plain_hashmap_entry *e;
17,364,207✔
1278
        unsigned hash, idx;
17,364,207✔
1279

1280
        assert(h);
17,364,207✔
1281

1282
        hash = bucket_hash(h, key);
17,364,207✔
1283
        idx = bucket_scan(h, hash, key);
17,364,207✔
1284
        if (idx != IDX_NIL) {
17,364,207✔
1285
                e = plain_bucket_at(h, idx);
340,750✔
1286
                if (e->value == value)
340,750✔
1287
                        return 0;
17,364,207✔
1288
                return -EEXIST;
38,404✔
1289
        }
1290

1291
        e = &bucket_at_swap(&swap, IDX_PUT)->p;
17,023,457✔
1292
        e->b.key = key;
17,023,457✔
1293
        e->value = value;
17,023,457✔
1294
        return hashmap_put_boldly(h, hash, &swap, true);
17,023,457✔
1295
}
1296

1297
int set_put(Set *s, const void *key) {
18,680,861✔
1298
        struct swap_entries swap;
18,680,861✔
1299
        struct hashmap_base_entry *e;
18,680,861✔
1300
        unsigned hash, idx;
18,680,861✔
1301

1302
        assert(s);
18,680,861✔
1303

1304
        hash = bucket_hash(s, key);
18,680,861✔
1305
        idx = bucket_scan(s, hash, key);
18,680,861✔
1306
        if (idx != IDX_NIL)
18,680,861✔
1307
                return 0;
18,680,861✔
1308

1309
        e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
18,661,794✔
1310
        e->key = key;
18,661,794✔
1311
        return hashmap_put_boldly(s, hash, &swap, true);
18,661,794✔
1312
}
1313

1314
int set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key) {
16,769,206✔
1315
        int r;
16,769,206✔
1316

1317
        assert(s);
16,769,206✔
1318

1319
        r = set_ensure_allocated(s, hash_ops);
16,769,206✔
1320
        if (r < 0)
16,769,206✔
1321
                return r;
1322

1323
        return set_put(*s, key);
16,769,206✔
1324
}
1325

1326
int set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key) {
1,468,663✔
1327
        int r;
1,468,663✔
1328

1329
        r = set_ensure_put(s, hash_ops, key);
1,468,663✔
1330
        if (r <= 0) {
1,468,663✔
1331
                if (hash_ops && hash_ops->free_key)
1,042✔
1332
                        hash_ops->free_key(key);
1,042✔
1333
                else
UNCOV
1334
                        free(key);
×
1335
        }
1336

1337
        return r;
1,468,663✔
1338
}
1339

1340
int hashmap_replace(Hashmap *h, const void *key, void *value) {
15,704,319✔
1341
        struct swap_entries swap;
15,704,319✔
1342
        struct plain_hashmap_entry *e;
15,704,319✔
1343
        unsigned hash, idx;
15,704,319✔
1344

1345
        assert(h);
15,704,319✔
1346

1347
        hash = bucket_hash(h, key);
15,704,319✔
1348
        idx = bucket_scan(h, hash, key);
15,704,319✔
1349
        if (idx != IDX_NIL) {
15,704,319✔
1350
                e = plain_bucket_at(h, idx);
1,166,254✔
1351
#if ENABLE_DEBUG_HASHMAP
1352
                /* Although the key is equal, the key pointer may have changed,
1353
                 * and this would break our assumption for iterating. So count
1354
                 * this operation as incompatible with iteration. */
1355
                if (e->b.key != key) {
1356
                        h->b.debug.put_count++;
1357
                        h->b.debug.rem_count++;
1358
                        h->b.debug.last_rem_idx = idx;
1359
                }
1360
#endif
1361
                e->b.key = key;
1,166,254✔
1362
                e->value = value;
1,166,254✔
1363
                hashmap_set_dirty(h);
1,166,254✔
1364

1365
                return 0;
1,166,254✔
1366
        }
1367

1368
        e = &bucket_at_swap(&swap, IDX_PUT)->p;
14,538,065✔
1369
        e->b.key = key;
14,538,065✔
1370
        e->value = value;
14,538,065✔
1371
        return hashmap_put_boldly(h, hash, &swap, true);
14,538,065✔
1372
}
1373

1374
int hashmap_update(Hashmap *h, const void *key, void *value) {
38,824✔
1375
        struct plain_hashmap_entry *e;
38,824✔
1376
        unsigned hash, idx;
38,824✔
1377

1378
        assert(h);
38,824✔
1379

1380
        hash = bucket_hash(h, key);
38,824✔
1381
        idx = bucket_scan(h, hash, key);
38,824✔
1382
        if (idx == IDX_NIL)
38,824✔
1383
                return -ENOENT;
1384

1385
        e = plain_bucket_at(h, idx);
38,822✔
1386
        e->value = value;
38,822✔
1387
        hashmap_set_dirty(h);
38,822✔
1388

1389
        return 0;
38,822✔
1390
}
1391

1392
void* _hashmap_get(HashmapBase *h, const void *key) {
133,430,386✔
1393
        struct hashmap_base_entry *e;
133,430,386✔
1394
        unsigned hash, idx;
133,430,386✔
1395

1396
        if (!h)
133,430,386✔
1397
                return NULL;
1398

1399
        hash = bucket_hash(h, key);
124,398,343✔
1400
        idx = bucket_scan(h, hash, key);
124,398,343✔
1401
        if (idx == IDX_NIL)
124,398,343✔
1402
                return NULL;
1403

1404
        e = bucket_at(h, idx);
94,016,255✔
1405
        return entry_value(h, e);
94,016,255✔
1406
}
1407

1408
void* hashmap_get2(Hashmap *h, const void *key, void **ret) {
13,215,182✔
1409
        struct plain_hashmap_entry *e;
13,215,182✔
1410
        unsigned hash, idx;
13,215,182✔
1411

1412
        if (!h)
13,215,182✔
1413
                return NULL;
1414

1415
        hash = bucket_hash(h, key);
13,215,151✔
1416
        idx = bucket_scan(h, hash, key);
13,215,151✔
1417
        if (idx == IDX_NIL)
13,215,151✔
1418
                return NULL;
1419

1420
        e = plain_bucket_at(h, idx);
1,075,010✔
1421
        if (ret)
1,075,010✔
1422
                *ret = (void*) e->b.key;
1,075,010✔
1423

1424
        return e->value;
1,075,010✔
1425
}
1426

1427
bool _hashmap_contains(HashmapBase *h, const void *key) {
43,428,900✔
1428
        unsigned hash;
43,428,900✔
1429

1430
        if (!h)
43,428,900✔
1431
                return false;
1432

1433
        hash = bucket_hash(h, key);
42,196,209✔
1434
        return bucket_scan(h, hash, key) != IDX_NIL;
42,196,209✔
1435
}
1436

1437
void* _hashmap_remove(HashmapBase *h, const void *key) {
26,722,013✔
1438
        struct hashmap_base_entry *e;
26,722,013✔
1439
        unsigned hash, idx;
26,722,013✔
1440
        void *data;
26,722,013✔
1441

1442
        if (!h)
26,722,013✔
1443
                return NULL;
1444

1445
        hash = bucket_hash(h, key);
25,487,018✔
1446
        idx = bucket_scan(h, hash, key);
25,487,018✔
1447
        if (idx == IDX_NIL)
25,487,018✔
1448
                return NULL;
1449

1450
        e = bucket_at(h, idx);
17,383,373✔
1451
        data = entry_value(h, e);
17,383,373✔
1452
        remove_entry(h, idx);
17,383,373✔
1453

1454
        return data;
17,383,373✔
1455
}
1456

1457
void* hashmap_remove2(Hashmap *h, const void *key, void **ret) {
596,345✔
1458
        struct plain_hashmap_entry *e;
596,345✔
1459
        unsigned hash, idx;
596,345✔
1460
        void *data;
596,345✔
1461

1462
        if (!h) {
596,345✔
1463
                if (ret)
90,493✔
1464
                        *ret = NULL;
90,493✔
1465
                return NULL;
1466
        }
1467

1468
        hash = bucket_hash(h, key);
505,852✔
1469
        idx = bucket_scan(h, hash, key);
505,852✔
1470
        if (idx == IDX_NIL) {
505,852✔
1471
                if (ret)
494,829✔
1472
                        *ret = NULL;
494,829✔
1473
                return NULL;
1474
        }
1475

1476
        e = plain_bucket_at(h, idx);
11,023✔
1477
        data = e->value;
11,023✔
1478
        if (ret)
11,023✔
1479
                *ret = (void*) e->b.key;
11,023✔
1480

1481
        remove_entry(h, idx);
11,023✔
1482

1483
        return data;
11,023✔
1484
}
1485

1486
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
10✔
1487
        struct swap_entries swap;
10✔
1488
        struct plain_hashmap_entry *e;
10✔
1489
        unsigned old_hash, new_hash, idx;
10✔
1490

1491
        if (!h)
10✔
1492
                return -ENOENT;
10✔
1493

1494
        old_hash = bucket_hash(h, old_key);
8✔
1495
        idx = bucket_scan(h, old_hash, old_key);
8✔
1496
        if (idx == IDX_NIL)
8✔
1497
                return -ENOENT;
1498

1499
        new_hash = bucket_hash(h, new_key);
6✔
1500
        if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
6✔
1501
                return -EEXIST;
1502

1503
        remove_entry(h, idx);
4✔
1504

1505
        e = &bucket_at_swap(&swap, IDX_PUT)->p;
4✔
1506
        e->b.key = new_key;
4✔
1507
        e->value = value;
4✔
1508
        assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
4✔
1509

1510
        return 0;
1511
}
1512

1513
int set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
1,293✔
1514
        struct swap_entries swap;
1,293✔
1515
        struct hashmap_base_entry *e;
1,293✔
1516
        unsigned old_hash, new_hash, idx;
1,293✔
1517

1518
        if (!s)
1,293✔
1519
                return -ENOENT;
1,293✔
1520

1521
        old_hash = bucket_hash(s, old_key);
1,293✔
1522
        idx = bucket_scan(s, old_hash, old_key);
1,293✔
1523
        if (idx == IDX_NIL)
1,293✔
1524
                return -ENOENT;
1525

1526
        new_hash = bucket_hash(s, new_key);
1,293✔
1527
        if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
1,293✔
1528
                return -EEXIST;
1529

1530
        remove_entry(s, idx);
1,293✔
1531

1532
        e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
1,293✔
1533
        e->key = new_key;
1,293✔
1534
        assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
1,293✔
1535

1536
        return 0;
1537
}
1538

1539
int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
48✔
1540
        struct swap_entries swap;
48✔
1541
        struct plain_hashmap_entry *e;
48✔
1542
        unsigned old_hash, new_hash, idx_old, idx_new;
48✔
1543

1544
        if (!h)
48✔
1545
                return -ENOENT;
48✔
1546

1547
        old_hash = bucket_hash(h, old_key);
46✔
1548
        idx_old = bucket_scan(h, old_hash, old_key);
46✔
1549
        if (idx_old == IDX_NIL)
46✔
1550
                return -ENOENT;
1551

1552
        old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
44✔
1553

1554
        new_hash = bucket_hash(h, new_key);
44✔
1555
        idx_new = bucket_scan(h, new_hash, new_key);
44✔
1556
        if (idx_new != IDX_NIL)
44✔
1557
                if (idx_old != idx_new) {
42✔
1558
                        remove_entry(h, idx_new);
42✔
1559
                        /* Compensate for a possible backward shift. */
1560
                        if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
42✔
1561
                                idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
8✔
1562
                        assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
42✔
1563
                }
1564

1565
        remove_entry(h, idx_old);
44✔
1566

1567
        e = &bucket_at_swap(&swap, IDX_PUT)->p;
44✔
1568
        e->b.key = new_key;
44✔
1569
        e->value = value;
44✔
1570
        assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
44✔
1571

1572
        return 0;
1573
}
1574

1575
void* _hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
831,626✔
1576
        struct hashmap_base_entry *e;
831,626✔
1577
        unsigned hash, idx;
831,626✔
1578

1579
        if (!h)
831,626✔
1580
                return NULL;
1581

1582
        hash = bucket_hash(h, key);
831,499✔
1583
        idx = bucket_scan(h, hash, key);
831,499✔
1584
        if (idx == IDX_NIL)
831,499✔
1585
                return NULL;
1586

1587
        e = bucket_at(h, idx);
766,076✔
1588
        if (entry_value(h, e) != value)
766,076✔
1589
                return NULL;
1590

1591
        remove_entry(h, idx);
765,757✔
1592

1593
        return value;
765,757✔
1594
}
1595

1596
static unsigned find_first_entry(HashmapBase *h) {
29,751,706✔
1597
        Iterator i = ITERATOR_FIRST;
29,751,706✔
1598

1599
        if (!h || !n_entries(h))
29,751,706✔
1600
                return IDX_NIL;
29,751,706✔
1601

1602
        return hashmap_iterate_entry(h, &i);
27,834,338✔
1603
}
1604

1605
void* _hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
29,751,706✔
1606
        struct hashmap_base_entry *e;
29,751,706✔
1607
        void *key, *data;
29,751,706✔
1608
        unsigned idx;
29,751,706✔
1609

1610
        idx = find_first_entry(h);
29,751,706✔
1611
        if (idx == IDX_NIL) {
29,751,706✔
1612
                if (ret_key)
1,917,368✔
1613
                        *ret_key = NULL;
1,014,017✔
1614
                return NULL;
1615
        }
1616

1617
        e = bucket_at(h, idx);
27,834,338✔
1618
        key = (void*) e->key;
27,834,338✔
1619
        data = entry_value(h, e);
27,834,338✔
1620

1621
        if (remove)
27,834,338✔
1622
                remove_entry(h, idx);
27,405,928✔
1623

1624
        if (ret_key)
27,834,338✔
1625
                *ret_key = key;
21,491,784✔
1626

1627
        return data;
1628
}
1629

1630
unsigned _hashmap_size(HashmapBase *h) {
69,587,587✔
1631
        if (!h)
69,587,587✔
1632
                return 0;
1633

1634
        return n_entries(h);
34,525,639✔
1635
}
1636

1637
unsigned _hashmap_buckets(HashmapBase *h) {
650✔
1638
        if (!h)
650✔
1639
                return 0;
1640

1641
        return n_buckets(h);
647✔
1642
}
1643

1644
int _hashmap_merge(Hashmap *h, Hashmap *other) {
4✔
1645
        Iterator i;
4✔
1646
        unsigned idx;
4✔
1647

1648
        assert(h);
4✔
1649

1650
        HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
16✔
1651
                struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
12✔
1652
                int r;
12✔
1653

1654
                r = hashmap_put(h, pe->b.key, pe->value);
12✔
1655
                if (r < 0 && r != -EEXIST)
12✔
1656
                        return r;
1657
        }
1658

1659
        return 0;
1660
}
1661

1662
int set_merge(Set *s, Set *other) {
1✔
1663
        Iterator i;
1✔
1664
        unsigned idx;
1✔
1665

1666
        assert(s);
1✔
1667

1668
        HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
5✔
1669
                struct set_entry *se = set_bucket_at(other, idx);
4✔
1670
                int r;
4✔
1671

1672
                r = set_put(s, se->b.key);
4✔
1673
                if (r < 0)
4✔
1674
                        return r;
1675
        }
1676

1677
        return 0;
1678
}
1679

1680
int _hashmap_reserve(HashmapBase *h, unsigned entries_add) {
9,518✔
1681
        int r;
9,518✔
1682

1683
        assert(h);
9,518✔
1684

1685
        r = resize_buckets(h, entries_add);
9,518✔
1686
        if (r < 0)
9,518✔
1687
                return r;
4✔
1688

1689
        return 0;
1690
}
1691

1692
/*
1693
 * The same as hashmap_merge(), but every new item from other is moved to h.
1694
 * Keys already in h are skipped and stay in other.
1695
 * Returns: 0 on success.
1696
 *          -ENOMEM on alloc failure, in which case no move has been done.
1697
 */
1698
int _hashmap_move(HashmapBase *h, HashmapBase *other) {
3,348✔
1699
        struct swap_entries swap;
3,348✔
1700
        struct hashmap_base_entry *e, *n;
3,348✔
1701
        Iterator i;
3,348✔
1702
        unsigned idx;
3,348✔
1703
        int r;
3,348✔
1704

1705
        assert(h);
3,348✔
1706

1707
        if (!other)
3,348✔
1708
                return 0;
3,348✔
1709

1710
        assert(other->type == h->type);
3,345✔
1711

1712
        /*
1713
         * This reserves buckets for the worst case, where none of other's
1714
         * entries are yet present in h. This is preferable to risking
1715
         * an allocation failure in the middle of the moving and having to
1716
         * rollback or return a partial result.
1717
         */
1718
        r = resize_buckets(h, n_entries(other));
3,345✔
1719
        if (r < 0)
3,345✔
1720
                return r;
1721

1722
        HASHMAP_FOREACH_IDX(idx, other, i) {
6,710✔
1723
                unsigned h_hash;
3,365✔
1724

1725
                e = bucket_at(other, idx);
3,365✔
1726
                h_hash = bucket_hash(h, e->key);
3,365✔
1727
                if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
3,365✔
1728
                        continue;
2✔
1729

1730
                n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
3,363✔
1731
                n->key = e->key;
3,363✔
1732
                if (h->type != HASHMAP_TYPE_SET)
3,363✔
1733
                        ((struct plain_hashmap_entry*) n)->value =
3,232✔
1734
                                ((struct plain_hashmap_entry*) e)->value;
3,232✔
1735
                assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
3,363✔
1736

1737
                remove_entry(other, idx);
3,363✔
1738
        }
1739

1740
        return 0;
1741
}
1742

1743
int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
3,488✔
1744
        struct swap_entries swap;
3,488✔
1745
        unsigned h_hash, other_hash, idx;
3,488✔
1746
        struct hashmap_base_entry *e, *n;
3,488✔
1747
        int r;
3,488✔
1748

1749
        assert(h);
3,488✔
1750

1751
        h_hash = bucket_hash(h, key);
3,488✔
1752
        if (bucket_scan(h, h_hash, key) != IDX_NIL)
3,488✔
1753
                return -EEXIST;
3,488✔
1754

1755
        if (!other)
3,486✔
1756
                return -ENOENT;
1757

1758
        assert(other->type == h->type);
3,484✔
1759

1760
        other_hash = bucket_hash(other, key);
3,484✔
1761
        idx = bucket_scan(other, other_hash, key);
3,484✔
1762
        if (idx == IDX_NIL)
3,484✔
1763
                return -ENOENT;
1764

1765
        e = bucket_at(other, idx);
3,482✔
1766

1767
        n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
3,482✔
1768
        n->key = e->key;
3,482✔
1769
        if (h->type != HASHMAP_TYPE_SET)
3,482✔
1770
                ((struct plain_hashmap_entry*) n)->value =
949✔
1771
                        ((struct plain_hashmap_entry*) e)->value;
949✔
1772
        r = hashmap_put_boldly(h, h_hash, &swap, true);
3,482✔
1773
        if (r < 0)
3,482✔
1774
                return r;
1775

1776
        remove_entry(other, idx);
3,482✔
1777
        return 0;
1778
}
1779

1780
HashmapBase* _hashmap_copy(HashmapBase *h) {
3✔
1781
        HashmapBase *copy;
3✔
1782
        int r;
3✔
1783

1784
        assert(h);
3✔
1785

1786
        copy = hashmap_base_new(h->hash_ops, h->type);
3✔
1787
        if (!copy)
3✔
1788
                return NULL;
1789

1790
        switch (h->type) {
3✔
1791
        case HASHMAP_TYPE_PLAIN:
2✔
1792
        case HASHMAP_TYPE_ORDERED:
1793
                r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
2✔
1794
                break;
2✔
1795
        case HASHMAP_TYPE_SET:
1✔
1796
                r = set_merge((Set*)copy, (Set*)h);
1✔
1797
                break;
1✔
UNCOV
1798
        default:
×
UNCOV
1799
                assert_not_reached();
×
1800
        }
1801

1802
        if (r < 0)
3✔
UNCOV
1803
                return _hashmap_free(copy);
×
1804

1805
        return copy;
1806
}
1807

1808
char** _hashmap_get_strv(HashmapBase *h) {
7,945✔
1809
        char **sv;
7,945✔
1810
        Iterator i;
7,945✔
1811
        unsigned idx, n;
7,945✔
1812

1813
        if (!h)
7,945✔
1814
                return new0(char*, 1);
7,901✔
1815

1816
        sv = new(char*, n_entries(h)+1);
44✔
1817
        if (!sv)
44✔
1818
                return NULL;
1819

1820
        n = 0;
44✔
1821
        HASHMAP_FOREACH_IDX(idx, h, i)
659✔
1822
                sv[n++] = entry_value(h, bucket_at(h, idx));
615✔
1823
        sv[n] = NULL;
44✔
1824

1825
        return sv;
44✔
1826
}
1827

1828
char** set_to_strv(Set **s) {
202✔
1829
        assert(s);
202✔
1830

1831
        /* This is similar to set_get_strv(), but invalidates the set on success. */
1832

1833
        char **v = new(char*, set_size(*s) + 1);
202✔
1834
        if (!v)
202✔
1835
                return NULL;
1836

1837
        for (char **p = v; (*p = set_steal_first(*s)); p++)
2,306✔
1838
                ;
1839

1840
        assert(set_isempty(*s));
202✔
1841
        *s = set_free(*s);
202✔
1842
        return v;
202✔
1843
}
1844

1845
void* ordered_hashmap_next(OrderedHashmap *h, const void *key) {
425✔
1846
        struct ordered_hashmap_entry *e;
425✔
1847
        unsigned hash, idx;
425✔
1848

1849
        if (!h)
425✔
1850
                return NULL;
1851

1852
        hash = bucket_hash(h, key);
424✔
1853
        idx = bucket_scan(h, hash, key);
424✔
1854
        if (idx == IDX_NIL)
424✔
1855
                return NULL;
1856

1857
        e = ordered_bucket_at(h, idx);
423✔
1858
        if (e->iterate_next == IDX_NIL)
423✔
1859
                return NULL;
1860
        return ordered_bucket_at(h, e->iterate_next)->p.value;
274✔
1861
}
1862

1863
int set_consume(Set *s, void *value) {
910,639✔
1864
        int r;
910,639✔
1865

1866
        assert(s);
910,639✔
1867
        assert(value);
910,639✔
1868

1869
        r = set_put(s, value);
910,639✔
1870
        if (r <= 0)
910,639✔
1871
                free(value);
1✔
1872

1873
        return r;
910,639✔
1874
}
1875

1876
int hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const char *k, const char *v) {
1,952✔
1877
        int r;
1,952✔
1878

1879
        assert(h);
1,952✔
1880

1881
        r = hashmap_ensure_allocated(h, hash_ops);
1,952✔
1882
        if (r < 0)
1,952✔
1883
                return r;
1,952✔
1884

1885
        _cleanup_free_ char *kdup = NULL, *vdup = NULL;
1,952✔
1886

1887
        kdup = strdup(k);
1,952✔
1888
        if (!kdup)
1,952✔
1889
                return -ENOMEM;
1890

1891
        if (v) {
1,952✔
1892
                vdup = strdup(v);
53✔
1893
                if (!vdup)
53✔
1894
                        return -ENOMEM;
1895
        }
1896

1897
        r = hashmap_put(*h, kdup, vdup);
1,952✔
1898
        if (r < 0) {
1,952✔
1899
                if (r == -EEXIST && streq_ptr(v, hashmap_get(*h, kdup)))
10✔
1900
                        return 0;
1901
                return r;
4✔
1902
        }
1903

1904
        /* 0 with non-null vdup would mean vdup is already in the hashmap, which cannot be */
1905
        assert(vdup == NULL || r > 0);
1,942✔
1906
        if (r > 0)
1,898✔
1907
                kdup = vdup = NULL;
1,941✔
1908

1909
        return r;
1910
}
1911

1912
int set_put_strndup_full(Set **s, const struct hash_ops *hash_ops, const char *p, size_t n) {
1,333,532✔
1913
        char *c;
1,333,532✔
1914
        int r;
1,333,532✔
1915

1916
        assert(s);
1,333,532✔
1917
        assert(p);
1,333,532✔
1918

1919
        r = set_ensure_allocated(s, hash_ops);
1,333,532✔
1920
        if (r < 0)
1,333,532✔
1921
                return r;
1922

1923
        if (n == SIZE_MAX) {
1,333,532✔
1924
                if (set_contains(*s, (char*) p))
1,308,631✔
1925
                        return 0;
1926

1927
                c = strdup(p);
877,553✔
1928
        } else
1929
                c = strndup(p, n);
24,901✔
1930
        if (!c)
902,454✔
1931
                return -ENOMEM;
1932

1933
        return set_consume(*s, c);
902,454✔
1934
}
1935

1936
int set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l) {
2,140✔
1937
        int n = 0, r;
2,140✔
1938

1939
        assert(s);
2,140✔
1940

1941
        STRV_FOREACH(i, l) {
2,183✔
1942
                r = set_put_strndup_full(s, hash_ops, *i, SIZE_MAX);
43✔
1943
                if (r < 0)
43✔
1944
                        return r;
1945

1946
                n += r;
43✔
1947
        }
1948

1949
        return n;
1950
}
1951

1952
int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) {
1✔
1953
        const char *p = ASSERT_PTR(v);
1✔
1954
        int r;
1✔
1955

1956
        assert(s);
1✔
1957

1958
        for (;;) {
2✔
1959
                char *word;
3✔
1960

1961
                r = extract_first_word(&p, &word, separators, flags);
3✔
1962
                if (r <= 0)
3✔
1963
                        return r;
1✔
1964

1965
                r = set_consume(s, word);
2✔
1966
                if (r < 0)
2✔
1967
                        return r;
1968
        }
1969
}
1970

1971
/* expand the cachemem if needed, return true if newly (re)activated. */
1972
static int cachemem_maintain(CacheMem *mem, size_t size) {
29,023,833✔
1973
        assert(mem);
29,023,833✔
1974

1975
        if (!GREEDY_REALLOC(mem->ptr, size)) {
29,023,833✔
UNCOV
1976
                if (size > 0)
×
1977
                        return -ENOMEM;
1978
        }
1979

1980
        if (!mem->active) {
29,023,833✔
1981
                mem->active = true;
2,878✔
1982
                return true;
2,878✔
1983
        }
1984

1985
        return false;
1986
}
1987

1988
int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) {
29,023,721✔
1989
        bool sync_keys = false, sync_values = false;
29,023,721✔
1990
        size_t size;
29,023,721✔
1991
        int r;
29,023,721✔
1992

1993
        assert(cache);
29,023,721✔
1994
        assert(cache->hashmap);
29,023,721✔
1995

1996
        size = n_entries(cache->hashmap);
29,023,721✔
1997

1998
        if (res_keys) {
29,023,721✔
1999
                r = cachemem_maintain(&cache->keys, size);
112✔
2000
                if (r < 0)
112✔
2001
                        return r;
2002

2003
                sync_keys = r;
112✔
2004
        } else
2005
                cache->keys.active = false;
29,023,609✔
2006

2007
        if (res_values) {
29,023,721✔
2008
                r = cachemem_maintain(&cache->values, size);
29,023,721✔
2009
                if (r < 0)
29,023,721✔
2010
                        return r;
2011

2012
                sync_values = r;
29,023,721✔
2013
        } else
UNCOV
2014
                cache->values.active = false;
×
2015

2016
        if (cache->hashmap->dirty) {
29,023,721✔
2017
                if (cache->keys.active)
2,985✔
2018
                        sync_keys = true;
111✔
2019
                if (cache->values.active)
2,985✔
2020
                        sync_values = true;
2,985✔
2021

2022
                cache->hashmap->dirty = false;
2,985✔
2023
        }
2024

2025
        if (sync_keys || sync_values) {
29,023,721✔
2026
                unsigned i, idx;
2,988✔
2027
                Iterator iter;
2,988✔
2028

2029
                i = 0;
2,988✔
2030
                HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) {
508,749✔
2031
                        struct hashmap_base_entry *e;
505,761✔
2032

2033
                        e = bucket_at(cache->hashmap, idx);
505,761✔
2034

2035
                        if (sync_keys)
505,761✔
2036
                                cache->keys.ptr[i] = e->key;
491,500✔
2037
                        if (sync_values)
505,761✔
2038
                                cache->values.ptr[i] = entry_value(cache->hashmap, e);
505,761✔
2039
                        i++;
505,761✔
2040
                }
2041
        }
2042

2043
        if (res_keys)
29,023,721✔
2044
                *res_keys = cache->keys.ptr;
112✔
2045
        if (res_values)
29,023,721✔
2046
                *res_values = cache->values.ptr;
29,023,721✔
2047
        if (res_n_entries)
29,023,721✔
2048
                *res_n_entries = size;
29,023,721✔
2049

2050
        return 0;
2051
}
2052

2053
IteratedCache* iterated_cache_free(IteratedCache *cache) {
7,504✔
2054
        if (cache) {
7,504✔
2055
                free(cache->keys.ptr);
7,504✔
2056
                free(cache->values.ptr);
7,504✔
2057
        }
2058

2059
        return mfree(cache);
7,504✔
2060
}
2061

2062
int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret) {
694,103✔
2063
        _cleanup_free_ char *str = NULL;
694,103✔
2064
        size_t separator_len, len = 0;
694,103✔
2065
        const char *value;
694,103✔
2066
        bool first;
694,103✔
2067

2068
        assert(ret);
694,103✔
2069

2070
        if (set_isempty(s)) {
694,103✔
2071
                *ret = NULL;
21,238✔
2072
                return 0;
21,238✔
2073
        }
2074

2075
        separator_len = strlen_ptr(separator);
672,865✔
2076

2077
        if (separator_len == 0)
672,861✔
2078
                wrap_with_separator = false;
2079

2080
        first = !wrap_with_separator;
672,865✔
2081

2082
        SET_FOREACH(value, s) {
2,145,860✔
2083
                size_t l = strlen_ptr(value);
1,472,995✔
2084

2085
                if (l == 0)
1,472,995✔
UNCOV
2086
                        continue;
×
2087

2088
                if (!GREEDY_REALLOC(str, len + l + (first ? 0 : separator_len) + (wrap_with_separator ? separator_len : 0) + 1))
3,742,124✔
UNCOV
2089
                        return -ENOMEM;
×
2090

2091
                if (separator_len > 0 && !first) {
1,472,995✔
2092
                        memcpy(str + len, separator, separator_len);
1,211,252✔
2093
                        len += separator_len;
1,211,252✔
2094
                }
2095

2096
                memcpy(str + len, value, l);
1,472,995✔
2097
                len += l;
1,472,995✔
2098
                first = false;
1,472,995✔
2099
        }
2100

2101
        if (wrap_with_separator) {
672,865✔
2102
                memcpy(str + len, separator, separator_len);
411,126✔
2103
                len += separator_len;
411,126✔
2104
        }
2105

2106
        str[len] = '\0';
672,865✔
2107

2108
        *ret = TAKE_PTR(str);
672,865✔
2109
        return 0;
672,865✔
2110
}
2111

2112
bool set_equal(Set *a, Set *b) {
265✔
2113
        void *p;
265✔
2114

2115
        /* Checks whether each entry of 'a' is also in 'b' and vice versa, i.e. the two sets contain the same
2116
         * entries */
2117

2118
        if (a == b)
265✔
2119
                return true;
265✔
2120

2121
        if (set_isempty(a) && set_isempty(b))
49✔
2122
                return true;
2123

2124
        if (set_size(a) != set_size(b)) /* Cheap check that hopefully catches a lot of inequality cases
31✔
2125
                                         * already */
2126
                return false;
2127

2128
        SET_FOREACH(p, a)
2,530✔
2129
                if (!set_contains(b, p))
2,510✔
UNCOV
2130
                        return false;
×
2131

2132
        /* If we have the same hashops, then we don't need to check things backwards given we compared the
2133
         * size and that all of a is in b. */
2134
        if (a->b.hash_ops == b->b.hash_ops)
20✔
2135
                return true;
2136

UNCOV
2137
        SET_FOREACH(p, b)
×
UNCOV
2138
                if (!set_contains(a, p))
×
UNCOV
2139
                        return false;
×
2140

UNCOV
2141
        return true;
×
2142
}
2143

2144
static bool set_fnmatch_one(Set *patterns, const char *needle) {
739,570✔
2145
        const char *p;
739,570✔
2146

2147
        assert(needle);
739,570✔
2148

2149
        /* Any failure of fnmatch() is treated as equivalent to FNM_NOMATCH, i.e. as non-matching pattern */
2150

2151
        SET_FOREACH(p, patterns)
954,790✔
2152
                if (fnmatch(p, needle, 0) == 0)
263,917✔
2153
                        return true;
48,697✔
2154

2155
        return false;
690,873✔
2156
}
2157

2158
bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needle) {
513,884✔
2159
        assert(needle);
513,884✔
2160

2161
        if (set_fnmatch_one(exclude_patterns, needle))
513,884✔
2162
                return false;
2163

2164
        if (set_isempty(include_patterns))
513,518✔
2165
                return true;
2166

2167
        return set_fnmatch_one(include_patterns, needle);
225,686✔
2168
}
2169

2170
static int hashmap_entry_compare(
756,570✔
2171
                struct hashmap_base_entry * const *a,
2172
                struct hashmap_base_entry * const *b,
2173
                compare_func_t compare) {
2174

2175
        assert(a && *a);
756,570✔
2176
        assert(b && *b);
756,570✔
2177
        assert(compare);
756,570✔
2178

2179
        return compare((*a)->key, (*b)->key);
756,570✔
2180
}
2181

2182
static int _hashmap_dump_entries_sorted(
154,965✔
2183
                HashmapBase *h,
2184
                void ***ret,
2185
                size_t *ret_n) {
2186
        _cleanup_free_ void **entries = NULL;
154,965✔
2187
        Iterator iter;
154,965✔
2188
        unsigned idx;
154,965✔
2189
        size_t n = 0;
154,965✔
2190

2191
        assert(ret);
154,965✔
2192
        assert(ret_n);
154,965✔
2193

2194
        if (_hashmap_size(h) == 0) {
154,965✔
2195
                *ret = NULL;
96,143✔
2196
                *ret_n = 0;
96,143✔
2197
                return 0;
96,143✔
2198
        }
2199

2200
        /* We append one more element than needed so that the resulting array can be used as a strv. We
2201
         * don't count this entry in the returned size. */
2202
        entries = new(void*, _hashmap_size(h) + 1);
58,822✔
2203
        if (!entries)
58,822✔
2204
                return -ENOMEM;
2205

2206
        HASHMAP_FOREACH_IDX(idx, h, iter)
313,454✔
2207
                entries[n++] = bucket_at(h, idx);
254,632✔
2208

2209
        assert(n == _hashmap_size(h));
58,822✔
2210
        entries[n] = NULL;
58,822✔
2211

2212
        typesafe_qsort_r((struct hashmap_base_entry**) entries, n,
58,822✔
2213
                         hashmap_entry_compare, h->hash_ops->compare);
2214

2215
        *ret = TAKE_PTR(entries);
58,822✔
2216
        *ret_n = n;
58,822✔
2217
        return 0;
58,822✔
2218
}
2219

2220
int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
743✔
2221
        _cleanup_free_ void **entries = NULL;
743✔
2222
        size_t n;
743✔
2223
        int r;
743✔
2224

2225
        assert(ret);
743✔
2226

2227
        r = _hashmap_dump_entries_sorted(h, &entries, &n);
743✔
2228
        if (r < 0)
743✔
2229
                return r;
2230

2231
        /* Reuse the array. */
2232
        FOREACH_ARRAY(e, entries, n)
5,180✔
2233
                *e = (void*) (*(struct hashmap_base_entry**) e)->key;
4,437✔
2234

2235
        *ret = TAKE_PTR(entries);
743✔
2236
        if (ret_n)
743✔
2237
                *ret_n = n;
743✔
2238
        return 0;
2239
}
2240

2241
int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
154,222✔
2242
        _cleanup_free_ void **entries = NULL;
154,222✔
2243
        size_t n;
154,222✔
2244
        int r;
154,222✔
2245

2246
        assert(ret);
154,222✔
2247

2248
        r = _hashmap_dump_entries_sorted(h, &entries, &n);
154,222✔
2249
        if (r < 0)
154,222✔
2250
                return r;
2251

2252
        /* Reuse the array. */
2253
        FOREACH_ARRAY(e, entries, n)
404,417✔
2254
                *e = entry_value(h, *(struct hashmap_base_entry**) e);
250,195✔
2255

2256
        *ret = TAKE_PTR(entries);
154,222✔
2257
        if (ret_n)
154,222✔
2258
                *ret_n = n;
151,542✔
2259
        return 0;
2260
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc