199ca880aSopenharmony_ci/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
299ca880aSopenharmony_ci
399ca880aSopenharmony_ci/***
499ca880aSopenharmony_ci  This file is part of systemd.
599ca880aSopenharmony_ci
699ca880aSopenharmony_ci  Copyright 2010 Lennart Poettering
799ca880aSopenharmony_ci  Copyright 2014 Michal Schmidt
899ca880aSopenharmony_ci
999ca880aSopenharmony_ci  systemd is free software; you can redistribute it and/or modify it
1099ca880aSopenharmony_ci  under the terms of the GNU Lesser General Public License as published by
1199ca880aSopenharmony_ci  the Free Software Foundation; either version 2.1 of the License, or
1299ca880aSopenharmony_ci  (at your option) any later version.
1399ca880aSopenharmony_ci
1499ca880aSopenharmony_ci  systemd is distributed in the hope that it will be useful, but
1599ca880aSopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1699ca880aSopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1799ca880aSopenharmony_ci  Lesser General Public License for more details.
1899ca880aSopenharmony_ci
1999ca880aSopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
2099ca880aSopenharmony_ci  along with systemd; If not, see <http://www.gnu.org/licenses/>.
2199ca880aSopenharmony_ci***/
2299ca880aSopenharmony_ci
2399ca880aSopenharmony_ci#include <assert.h>
2499ca880aSopenharmony_ci#include <stdlib.h>
2599ca880aSopenharmony_ci#include <string.h>
2699ca880aSopenharmony_ci#include <errno.h>
2799ca880aSopenharmony_ci
2899ca880aSopenharmony_ci#include "util.h"
2999ca880aSopenharmony_ci#include "hashmap.h"
3099ca880aSopenharmony_ci#include "set.h"
3199ca880aSopenharmony_ci#include "macro.h"
3299ca880aSopenharmony_ci#include "siphash24.h"
3399ca880aSopenharmony_ci#include "strv.h"
3499ca880aSopenharmony_ci#include "list.h"
3599ca880aSopenharmony_ci#include "mempool.h"
3699ca880aSopenharmony_ci#include "random-util.h"
3799ca880aSopenharmony_ci
3899ca880aSopenharmony_ci/*
3999ca880aSopenharmony_ci * Implementation of hashmaps.
4099ca880aSopenharmony_ci * Addressing: open
4199ca880aSopenharmony_ci *   - uses less RAM compared to closed addressing (chaining), because
4299ca880aSopenharmony_ci *     our entries are small (especially in Sets, which tend to contain
4399ca880aSopenharmony_ci *     the majority of entries in systemd).
4499ca880aSopenharmony_ci * Collision resolution: Robin Hood
4599ca880aSopenharmony_ci *   - tends to equalize displacement of entries from their optimal buckets.
4699ca880aSopenharmony_ci * Probe sequence: linear
4799ca880aSopenharmony_ci *   - though theoretically worse than random probing/uniform hashing/double
4899ca880aSopenharmony_ci *     hashing, it is good for cache locality.
4999ca880aSopenharmony_ci *
5099ca880aSopenharmony_ci * References:
5199ca880aSopenharmony_ci * Celis, P. 1986. Robin Hood Hashing.
5299ca880aSopenharmony_ci * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
5399ca880aSopenharmony_ci * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
5499ca880aSopenharmony_ci * - The results are derived for random probing. Suggests deletion with
5599ca880aSopenharmony_ci *   tombstones and two mean-centered search methods. None of that works
5699ca880aSopenharmony_ci *   well for linear probing.
5799ca880aSopenharmony_ci *
5899ca880aSopenharmony_ci * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
5999ca880aSopenharmony_ci * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
6099ca880aSopenharmony_ci * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
6199ca880aSopenharmony_ci * http://www.math.uu.se/~svante/papers/sj157.pdf
6299ca880aSopenharmony_ci * - Applies to Robin Hood with linear probing. Contains remarks on
6399ca880aSopenharmony_ci *   the unsuitability of mean-centered search with linear probing.
6499ca880aSopenharmony_ci *
6599ca880aSopenharmony_ci * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
6699ca880aSopenharmony_ci * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
6799ca880aSopenharmony_ci * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
6899ca880aSopenharmony_ci * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
6999ca880aSopenharmony_ci *   in a successful search), and Janson writes about displacement. C = d + 1.
7099ca880aSopenharmony_ci *
7199ca880aSopenharmony_ci * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
7299ca880aSopenharmony_ci * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
7399ca880aSopenharmony_ci * - Explanation of backward shift deletion with pictures.
7499ca880aSopenharmony_ci *
7599ca880aSopenharmony_ci * Khuong, P. 2013. The Other Robin Hood Hashing.
7699ca880aSopenharmony_ci * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
7799ca880aSopenharmony_ci * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
7899ca880aSopenharmony_ci */
7999ca880aSopenharmony_ci
8099ca880aSopenharmony_ci/*
8199ca880aSopenharmony_ci * XXX Ideas for improvement:
8299ca880aSopenharmony_ci * For unordered hashmaps, randomize iteration order, similarly to Perl:
8399ca880aSopenharmony_ci * http://blog.booking.com/hardening-perls-hash-function.html
8499ca880aSopenharmony_ci */
8599ca880aSopenharmony_ci
8699ca880aSopenharmony_ci/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
8799ca880aSopenharmony_ci * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
8899ca880aSopenharmony_ci#define INV_KEEP_FREE            5U
8999ca880aSopenharmony_ci
9099ca880aSopenharmony_ci/* Fields common to entries of all hashmap/set types */
9199ca880aSopenharmony_cistruct hashmap_base_entry {
9299ca880aSopenharmony_ci        const void *key;
9399ca880aSopenharmony_ci};
9499ca880aSopenharmony_ci
9599ca880aSopenharmony_ci/* Entry types for specific hashmap/set types
9699ca880aSopenharmony_ci * hashmap_base_entry must be at the beginning of each entry struct. */
9799ca880aSopenharmony_ci
9899ca880aSopenharmony_cistruct plain_hashmap_entry {
9999ca880aSopenharmony_ci        struct hashmap_base_entry b;
10099ca880aSopenharmony_ci        void *value;
10199ca880aSopenharmony_ci};
10299ca880aSopenharmony_ci
10399ca880aSopenharmony_cistruct ordered_hashmap_entry {
10499ca880aSopenharmony_ci        struct plain_hashmap_entry p;
10599ca880aSopenharmony_ci        unsigned iterate_next, iterate_previous;
10699ca880aSopenharmony_ci};
10799ca880aSopenharmony_ci
10899ca880aSopenharmony_cistruct set_entry {
10999ca880aSopenharmony_ci        struct hashmap_base_entry b;
11099ca880aSopenharmony_ci};
11199ca880aSopenharmony_ci
11299ca880aSopenharmony_ci/* In several functions it is advantageous to have the hash table extended
11399ca880aSopenharmony_ci * virtually by a couple of additional buckets. We reserve special index values
11499ca880aSopenharmony_ci * for these "swap" buckets. */
11599ca880aSopenharmony_ci#define _IDX_SWAP_BEGIN     (UINT_MAX - 3)
11699ca880aSopenharmony_ci#define IDX_PUT             (_IDX_SWAP_BEGIN + 0)
11799ca880aSopenharmony_ci#define IDX_TMP             (_IDX_SWAP_BEGIN + 1)
11899ca880aSopenharmony_ci#define _IDX_SWAP_END       (_IDX_SWAP_BEGIN + 2)
11999ca880aSopenharmony_ci
12099ca880aSopenharmony_ci#define IDX_FIRST           (UINT_MAX - 1) /* special index for freshly initialized iterators */
12199ca880aSopenharmony_ci#define IDX_NIL             UINT_MAX       /* special index value meaning "none" or "end" */
12299ca880aSopenharmony_ci
12399ca880aSopenharmony_ciassert_cc(IDX_FIRST == _IDX_SWAP_END);
12499ca880aSopenharmony_ciassert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
12599ca880aSopenharmony_ci
12699ca880aSopenharmony_ci/* Storage space for the "swap" buckets.
12799ca880aSopenharmony_ci * All entry types can fit into a ordered_hashmap_entry. */
12899ca880aSopenharmony_cistruct swap_entries {
12999ca880aSopenharmony_ci        struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
13099ca880aSopenharmony_ci};
13199ca880aSopenharmony_ci
13299ca880aSopenharmony_ci/* Distance from Initial Bucket */
13399ca880aSopenharmony_citypedef uint8_t dib_raw_t;
13499ca880aSopenharmony_ci#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU)   /* indicates DIB value is greater than representable */
13599ca880aSopenharmony_ci#define DIB_RAW_REHASH   ((dib_raw_t)0xfeU)   /* entry yet to be rehashed during in-place resize */
13699ca880aSopenharmony_ci#define DIB_RAW_FREE     ((dib_raw_t)0xffU)   /* a free bucket */
13799ca880aSopenharmony_ci#define DIB_RAW_INIT     ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
13899ca880aSopenharmony_ci
13999ca880aSopenharmony_ci#define DIB_FREE UINT_MAX
14099ca880aSopenharmony_ci
14199ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
14299ca880aSopenharmony_cistruct hashmap_debug_info {
14399ca880aSopenharmony_ci        LIST_FIELDS(struct hashmap_debug_info, debug_list);
14499ca880aSopenharmony_ci        unsigned max_entries;  /* high watermark of n_entries */
14599ca880aSopenharmony_ci
14699ca880aSopenharmony_ci        /* who allocated this hashmap */
14799ca880aSopenharmony_ci        int line;
14899ca880aSopenharmony_ci        const char *file;
14999ca880aSopenharmony_ci        const char *func;
15099ca880aSopenharmony_ci
15199ca880aSopenharmony_ci        /* fields to detect modification while iterating */
15299ca880aSopenharmony_ci        unsigned put_count;    /* counts puts into the hashmap */
15399ca880aSopenharmony_ci        unsigned rem_count;    /* counts removals from hashmap */
15499ca880aSopenharmony_ci        unsigned last_rem_idx; /* remembers last removal index */
15599ca880aSopenharmony_ci};
15699ca880aSopenharmony_ci
15799ca880aSopenharmony_ci/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
15899ca880aSopenharmony_cistatic LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
15999ca880aSopenharmony_ci
16099ca880aSopenharmony_ci#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
16199ca880aSopenharmony_ci
16299ca880aSopenharmony_ci#else /* !ENABLE_DEBUG_HASHMAP */
16399ca880aSopenharmony_ci#define HASHMAP_DEBUG_FIELDS
16499ca880aSopenharmony_ci#endif /* ENABLE_DEBUG_HASHMAP */
16599ca880aSopenharmony_ci
16699ca880aSopenharmony_cienum HashmapType {
16799ca880aSopenharmony_ci        HASHMAP_TYPE_PLAIN,
16899ca880aSopenharmony_ci        HASHMAP_TYPE_ORDERED,
16999ca880aSopenharmony_ci        HASHMAP_TYPE_SET,
17099ca880aSopenharmony_ci        _HASHMAP_TYPE_MAX
17199ca880aSopenharmony_ci};
17299ca880aSopenharmony_ci
17399ca880aSopenharmony_cistruct _packed_ indirect_storage {
17499ca880aSopenharmony_ci        char    *storage;                  /* where buckets and DIBs are stored */
17599ca880aSopenharmony_ci        uint8_t  hash_key[HASH_KEY_SIZE];  /* hash key; changes during resize */
17699ca880aSopenharmony_ci
17799ca880aSopenharmony_ci        unsigned n_entries;                /* number of stored entries */
17899ca880aSopenharmony_ci        unsigned n_buckets;                /* number of buckets */
17999ca880aSopenharmony_ci
18099ca880aSopenharmony_ci        unsigned idx_lowest_entry;         /* Index below which all buckets are free.
18199ca880aSopenharmony_ci                                              Makes "while(hashmap_steal_first())" loops
18299ca880aSopenharmony_ci                                              O(n) instead of O(n^2) for unordered hashmaps. */
18399ca880aSopenharmony_ci        uint8_t  _pad[3];                  /* padding for the whole HashmapBase */
18499ca880aSopenharmony_ci        /* The bitfields in HashmapBase complete the alignment of the whole thing. */
18599ca880aSopenharmony_ci};
18699ca880aSopenharmony_ci
18799ca880aSopenharmony_cistruct direct_storage {
18899ca880aSopenharmony_ci        /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
18999ca880aSopenharmony_ci         * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
19099ca880aSopenharmony_ci         *              or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
19199ca880aSopenharmony_ci        char storage[sizeof(struct indirect_storage)];
19299ca880aSopenharmony_ci};
19399ca880aSopenharmony_ci
19499ca880aSopenharmony_ci#define DIRECT_BUCKETS(entry_t) \
19599ca880aSopenharmony_ci        (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
19699ca880aSopenharmony_ci
19799ca880aSopenharmony_ci/* We should be able to store at least one entry directly. */
19899ca880aSopenharmony_ciassert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
19999ca880aSopenharmony_ci
20099ca880aSopenharmony_ci/* We have 3 bits for n_direct_entries. */
20199ca880aSopenharmony_ciassert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
20299ca880aSopenharmony_ci
20399ca880aSopenharmony_ci/* Hashmaps with directly stored entries all use this shared hash key.
20499ca880aSopenharmony_ci * It's no big deal if the key is guessed, because there can be only
20599ca880aSopenharmony_ci * a handful of directly stored entries in a hashmap. When a hashmap
20699ca880aSopenharmony_ci * outgrows direct storage, it gets its own key for indirect storage. */
20799ca880aSopenharmony_cistatic uint8_t shared_hash_key[HASH_KEY_SIZE];
20899ca880aSopenharmony_cistatic bool shared_hash_key_initialized;
20999ca880aSopenharmony_ci
21099ca880aSopenharmony_ci/* Fields that all hashmap/set types must have */
21199ca880aSopenharmony_cistruct HashmapBase {
21299ca880aSopenharmony_ci        const struct hash_ops *hash_ops;  /* hash and compare ops to use */
21399ca880aSopenharmony_ci
21499ca880aSopenharmony_ci        union _packed_ {
21599ca880aSopenharmony_ci                struct indirect_storage indirect; /* if  has_indirect */
21699ca880aSopenharmony_ci                struct direct_storage direct;     /* if !has_indirect */
21799ca880aSopenharmony_ci        };
21899ca880aSopenharmony_ci
21999ca880aSopenharmony_ci        enum HashmapType type:2;     /* HASHMAP_TYPE_* */
22099ca880aSopenharmony_ci        bool has_indirect:1;         /* whether indirect storage is used */
22199ca880aSopenharmony_ci        unsigned n_direct_entries:3; /* Number of entries in direct storage.
22299ca880aSopenharmony_ci                                      * Only valid if !has_indirect. */
22399ca880aSopenharmony_ci        bool from_pool:1;            /* whether was allocated from mempool */
22499ca880aSopenharmony_ci        HASHMAP_DEBUG_FIELDS         /* optional hashmap_debug_info */
22599ca880aSopenharmony_ci};
22699ca880aSopenharmony_ci
22799ca880aSopenharmony_ci/* Specific hash types
22899ca880aSopenharmony_ci * HashmapBase must be at the beginning of each hashmap struct. */
22999ca880aSopenharmony_ci
23099ca880aSopenharmony_cistruct Hashmap {
23199ca880aSopenharmony_ci        struct HashmapBase b;
23299ca880aSopenharmony_ci};
23399ca880aSopenharmony_ci
23499ca880aSopenharmony_cistruct OrderedHashmap {
23599ca880aSopenharmony_ci        struct HashmapBase b;
23699ca880aSopenharmony_ci        unsigned iterate_list_head, iterate_list_tail;
23799ca880aSopenharmony_ci};
23899ca880aSopenharmony_ci
23999ca880aSopenharmony_cistruct Set {
24099ca880aSopenharmony_ci        struct HashmapBase b;
24199ca880aSopenharmony_ci};
24299ca880aSopenharmony_ci
24399ca880aSopenharmony_ciDEFINE_MEMPOOL(hashmap_pool,         Hashmap,        8);
24499ca880aSopenharmony_ciDEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
24599ca880aSopenharmony_ci/* No need for a separate Set pool */
24699ca880aSopenharmony_ciassert_cc(sizeof(Hashmap) == sizeof(Set));
24799ca880aSopenharmony_ci
24899ca880aSopenharmony_cistruct hashmap_type_info {
24999ca880aSopenharmony_ci        size_t head_size;
25099ca880aSopenharmony_ci        size_t entry_size;
25199ca880aSopenharmony_ci        struct mempool *mempool;
25299ca880aSopenharmony_ci        unsigned n_direct_buckets;
25399ca880aSopenharmony_ci};
25499ca880aSopenharmony_ci
25599ca880aSopenharmony_cistatic const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
25699ca880aSopenharmony_ci        [HASHMAP_TYPE_PLAIN] = {
25799ca880aSopenharmony_ci                .head_size        = sizeof(Hashmap),
25899ca880aSopenharmony_ci                .entry_size       = sizeof(struct plain_hashmap_entry),
25999ca880aSopenharmony_ci                .mempool          = &hashmap_pool,
26099ca880aSopenharmony_ci                .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
26199ca880aSopenharmony_ci        },
26299ca880aSopenharmony_ci        [HASHMAP_TYPE_ORDERED] = {
26399ca880aSopenharmony_ci                .head_size        = sizeof(OrderedHashmap),
26499ca880aSopenharmony_ci                .entry_size       = sizeof(struct ordered_hashmap_entry),
26599ca880aSopenharmony_ci                .mempool          = &ordered_hashmap_pool,
26699ca880aSopenharmony_ci                .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
26799ca880aSopenharmony_ci        },
26899ca880aSopenharmony_ci        [HASHMAP_TYPE_SET] = {
26999ca880aSopenharmony_ci                .head_size        = sizeof(Set),
27099ca880aSopenharmony_ci                .entry_size       = sizeof(struct set_entry),
27199ca880aSopenharmony_ci                .mempool          = &hashmap_pool,
27299ca880aSopenharmony_ci                .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
27399ca880aSopenharmony_ci        },
27499ca880aSopenharmony_ci};
27599ca880aSopenharmony_ci
27699ca880aSopenharmony_ciunsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
27799ca880aSopenharmony_ci        uint64_t u;
27899ca880aSopenharmony_ci        siphash24((uint8_t*) &u, p, strlen(p), hash_key);
27999ca880aSopenharmony_ci        return (unsigned long) u;
28099ca880aSopenharmony_ci}
28199ca880aSopenharmony_ci
28299ca880aSopenharmony_ciint string_compare_func(const void *a, const void *b) {
28399ca880aSopenharmony_ci        return strcmp(a, b);
28499ca880aSopenharmony_ci}
28599ca880aSopenharmony_ci
28699ca880aSopenharmony_ciconst struct hash_ops string_hash_ops = {
28799ca880aSopenharmony_ci        .hash = string_hash_func,
28899ca880aSopenharmony_ci        .compare = string_compare_func
28999ca880aSopenharmony_ci};
29099ca880aSopenharmony_ci
29199ca880aSopenharmony_ciunsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
29299ca880aSopenharmony_ci        uint64_t u;
29399ca880aSopenharmony_ci        siphash24((uint8_t*) &u, &p, sizeof(p), hash_key);
29499ca880aSopenharmony_ci        return (unsigned long) u;
29599ca880aSopenharmony_ci}
29699ca880aSopenharmony_ci
29799ca880aSopenharmony_ciint trivial_compare_func(const void *a, const void *b) {
29899ca880aSopenharmony_ci        return a < b ? -1 : (a > b ? 1 : 0);
29999ca880aSopenharmony_ci}
30099ca880aSopenharmony_ci
30199ca880aSopenharmony_ciconst struct hash_ops trivial_hash_ops = {
30299ca880aSopenharmony_ci        .hash = trivial_hash_func,
30399ca880aSopenharmony_ci        .compare = trivial_compare_func
30499ca880aSopenharmony_ci};
30599ca880aSopenharmony_ci
30699ca880aSopenharmony_ciunsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
30799ca880aSopenharmony_ci        uint64_t u;
30899ca880aSopenharmony_ci        siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key);
30999ca880aSopenharmony_ci        return (unsigned long) u;
31099ca880aSopenharmony_ci}
31199ca880aSopenharmony_ci
31299ca880aSopenharmony_ciint uint64_compare_func(const void *_a, const void *_b) {
31399ca880aSopenharmony_ci        uint64_t a, b;
31499ca880aSopenharmony_ci        a = *(const uint64_t*) _a;
31599ca880aSopenharmony_ci        b = *(const uint64_t*) _b;
31699ca880aSopenharmony_ci        return a < b ? -1 : (a > b ? 1 : 0);
31799ca880aSopenharmony_ci}
31899ca880aSopenharmony_ci
31999ca880aSopenharmony_ciconst struct hash_ops uint64_hash_ops = {
32099ca880aSopenharmony_ci        .hash = uint64_hash_func,
32199ca880aSopenharmony_ci        .compare = uint64_compare_func
32299ca880aSopenharmony_ci};
32399ca880aSopenharmony_ci
32499ca880aSopenharmony_ci#if SIZEOF_DEV_T != 8
32599ca880aSopenharmony_ciunsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
32699ca880aSopenharmony_ci        uint64_t u;
32799ca880aSopenharmony_ci        siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key);
32899ca880aSopenharmony_ci        return (unsigned long) u;
32999ca880aSopenharmony_ci}
33099ca880aSopenharmony_ci
33199ca880aSopenharmony_ciint devt_compare_func(const void *_a, const void *_b) {
33299ca880aSopenharmony_ci        dev_t a, b;
33399ca880aSopenharmony_ci        a = *(const dev_t*) _a;
33499ca880aSopenharmony_ci        b = *(const dev_t*) _b;
33599ca880aSopenharmony_ci        return a < b ? -1 : (a > b ? 1 : 0);
33699ca880aSopenharmony_ci}
33799ca880aSopenharmony_ci
33899ca880aSopenharmony_ciconst struct hash_ops devt_hash_ops = {
33999ca880aSopenharmony_ci        .hash = devt_hash_func,
34099ca880aSopenharmony_ci        .compare = devt_compare_func
34199ca880aSopenharmony_ci};
34299ca880aSopenharmony_ci#endif
34399ca880aSopenharmony_ci
34499ca880aSopenharmony_cistatic unsigned n_buckets(HashmapBase *h) {
34599ca880aSopenharmony_ci        return h->has_indirect ? h->indirect.n_buckets
34699ca880aSopenharmony_ci                               : hashmap_type_info[h->type].n_direct_buckets;
34799ca880aSopenharmony_ci}
34899ca880aSopenharmony_ci
34999ca880aSopenharmony_cistatic unsigned n_entries(HashmapBase *h) {
35099ca880aSopenharmony_ci        return h->has_indirect ? h->indirect.n_entries
35199ca880aSopenharmony_ci                               : h->n_direct_entries;
35299ca880aSopenharmony_ci}
35399ca880aSopenharmony_ci
35499ca880aSopenharmony_cistatic void n_entries_inc(HashmapBase *h) {
35599ca880aSopenharmony_ci        if (h->has_indirect)
35699ca880aSopenharmony_ci                h->indirect.n_entries++;
35799ca880aSopenharmony_ci        else
35899ca880aSopenharmony_ci                h->n_direct_entries++;
35999ca880aSopenharmony_ci}
36099ca880aSopenharmony_ci
36199ca880aSopenharmony_cistatic void n_entries_dec(HashmapBase *h) {
36299ca880aSopenharmony_ci        if (h->has_indirect)
36399ca880aSopenharmony_ci                h->indirect.n_entries--;
36499ca880aSopenharmony_ci        else
36599ca880aSopenharmony_ci                h->n_direct_entries--;
36699ca880aSopenharmony_ci}
36799ca880aSopenharmony_ci
36899ca880aSopenharmony_cistatic char *storage_ptr(HashmapBase *h) {
36999ca880aSopenharmony_ci        return h->has_indirect ? h->indirect.storage
37099ca880aSopenharmony_ci                               : h->direct.storage;
37199ca880aSopenharmony_ci}
37299ca880aSopenharmony_ci
37399ca880aSopenharmony_cistatic uint8_t *hash_key(HashmapBase *h) {
37499ca880aSopenharmony_ci        return h->has_indirect ? h->indirect.hash_key
37599ca880aSopenharmony_ci                               : shared_hash_key;
37699ca880aSopenharmony_ci}
37799ca880aSopenharmony_ci
37899ca880aSopenharmony_cistatic unsigned base_bucket_hash(HashmapBase *h, const void *p) {
37999ca880aSopenharmony_ci        return (unsigned) (h->hash_ops->hash(p, hash_key(h)) % n_buckets(h));
38099ca880aSopenharmony_ci}
38199ca880aSopenharmony_ci#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
38299ca880aSopenharmony_ci
38399ca880aSopenharmony_cistatic void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
38499ca880aSopenharmony_ci        static uint8_t current[HASH_KEY_SIZE];
38599ca880aSopenharmony_ci        static bool current_initialized = false;
38699ca880aSopenharmony_ci
38799ca880aSopenharmony_ci        /* Returns a hash function key to use. In order to keep things
38899ca880aSopenharmony_ci         * fast we will not generate a new key each time we allocate a
38999ca880aSopenharmony_ci         * new hash table. Instead, we'll just reuse the most recently
39099ca880aSopenharmony_ci         * generated one, except if we never generated one or when we
39199ca880aSopenharmony_ci         * are rehashing an entire hash table because we reached a
39299ca880aSopenharmony_ci         * fill level */
39399ca880aSopenharmony_ci
39499ca880aSopenharmony_ci        if (!current_initialized || !reuse_is_ok) {
39599ca880aSopenharmony_ci                random_bytes(current, sizeof(current));
39699ca880aSopenharmony_ci                current_initialized = true;
39799ca880aSopenharmony_ci        }
39899ca880aSopenharmony_ci
39999ca880aSopenharmony_ci        memcpy(hash_key, current, sizeof(current));
40099ca880aSopenharmony_ci}
40199ca880aSopenharmony_ci
40299ca880aSopenharmony_cistatic struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
40399ca880aSopenharmony_ci        return (struct hashmap_base_entry*)
40499ca880aSopenharmony_ci                (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
40599ca880aSopenharmony_ci}
40699ca880aSopenharmony_ci
40799ca880aSopenharmony_cistatic struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
40899ca880aSopenharmony_ci        return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
40999ca880aSopenharmony_ci}
41099ca880aSopenharmony_ci
41199ca880aSopenharmony_cistatic struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
41299ca880aSopenharmony_ci        return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
41399ca880aSopenharmony_ci}
41499ca880aSopenharmony_ci
41599ca880aSopenharmony_cistatic struct set_entry *set_bucket_at(Set *h, unsigned idx) {
41699ca880aSopenharmony_ci        return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
41799ca880aSopenharmony_ci}
41899ca880aSopenharmony_ci
41999ca880aSopenharmony_cistatic struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
42099ca880aSopenharmony_ci        return &swap->e[idx - _IDX_SWAP_BEGIN];
42199ca880aSopenharmony_ci}
42299ca880aSopenharmony_ci
42399ca880aSopenharmony_ci/* Returns a pointer to the bucket at index idx.
42499ca880aSopenharmony_ci * Understands real indexes and swap indexes, hence "_virtual". */
42599ca880aSopenharmony_cistatic struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
42699ca880aSopenharmony_ci                                                    unsigned idx) {
42799ca880aSopenharmony_ci        if (idx < _IDX_SWAP_BEGIN)
42899ca880aSopenharmony_ci                return bucket_at(h, idx);
42999ca880aSopenharmony_ci
43099ca880aSopenharmony_ci        if (idx < _IDX_SWAP_END)
43199ca880aSopenharmony_ci                return &bucket_at_swap(swap, idx)->p.b;
43299ca880aSopenharmony_ci
43399ca880aSopenharmony_ci        assert_not_reached("Invalid index");
43499ca880aSopenharmony_ci}
43599ca880aSopenharmony_ci
43699ca880aSopenharmony_cistatic dib_raw_t *dib_raw_ptr(HashmapBase *h) {
43799ca880aSopenharmony_ci        return (dib_raw_t*)
43899ca880aSopenharmony_ci                (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
43999ca880aSopenharmony_ci}
44099ca880aSopenharmony_ci
44199ca880aSopenharmony_cistatic unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
44299ca880aSopenharmony_ci        return idx >= from ? idx - from
44399ca880aSopenharmony_ci                           : n_buckets(h) + idx - from;
44499ca880aSopenharmony_ci}
44599ca880aSopenharmony_ci
44699ca880aSopenharmony_cistatic unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
44799ca880aSopenharmony_ci        unsigned initial_bucket;
44899ca880aSopenharmony_ci
44999ca880aSopenharmony_ci        if (raw_dib == DIB_RAW_FREE)
45099ca880aSopenharmony_ci                return DIB_FREE;
45199ca880aSopenharmony_ci
45299ca880aSopenharmony_ci        if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
45399ca880aSopenharmony_ci                return raw_dib;
45499ca880aSopenharmony_ci
45599ca880aSopenharmony_ci        /*
45699ca880aSopenharmony_ci         * Having an overflow DIB value is very unlikely. The hash function
45799ca880aSopenharmony_ci         * would have to be bad. For example, in a table of size 2^24 filled
45899ca880aSopenharmony_ci         * to load factor 0.9 the maximum observed DIB is only about 60.
45999ca880aSopenharmony_ci         * In theory (assuming I used Maxima correctly), for an infinite size
46099ca880aSopenharmony_ci         * hash table with load factor 0.8 the probability of a given entry
46199ca880aSopenharmony_ci         * having DIB > 40 is 1.9e-8.
46299ca880aSopenharmony_ci         * This returns the correct DIB value by recomputing the hash value in
46399ca880aSopenharmony_ci         * the unlikely case. XXX Hitting this case could be a hint to rehash.
46499ca880aSopenharmony_ci         */
46599ca880aSopenharmony_ci        initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
46699ca880aSopenharmony_ci        return bucket_distance(h, idx, initial_bucket);
46799ca880aSopenharmony_ci}
46899ca880aSopenharmony_ci
46999ca880aSopenharmony_cistatic void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
47099ca880aSopenharmony_ci        dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
47199ca880aSopenharmony_ci}
47299ca880aSopenharmony_ci
47399ca880aSopenharmony_cistatic unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
47499ca880aSopenharmony_ci        dib_raw_t *dibs;
47599ca880aSopenharmony_ci
47699ca880aSopenharmony_ci        dibs = dib_raw_ptr(h);
47799ca880aSopenharmony_ci
47899ca880aSopenharmony_ci        for ( ; idx < n_buckets(h); idx++)
47999ca880aSopenharmony_ci                if (dibs[idx] != DIB_RAW_FREE)
48099ca880aSopenharmony_ci                        return idx;
48199ca880aSopenharmony_ci
48299ca880aSopenharmony_ci        return IDX_NIL;
48399ca880aSopenharmony_ci}
48499ca880aSopenharmony_ci
48599ca880aSopenharmony_cistatic void bucket_mark_free(HashmapBase *h, unsigned idx) {
48699ca880aSopenharmony_ci        memset(bucket_at(h, idx), 0, hashmap_type_info[h->type].entry_size);
48799ca880aSopenharmony_ci        bucket_set_dib(h, idx, DIB_FREE);
48899ca880aSopenharmony_ci}
48999ca880aSopenharmony_ci
49099ca880aSopenharmony_cistatic void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
49199ca880aSopenharmony_ci                              unsigned from, unsigned to) {
49299ca880aSopenharmony_ci        struct hashmap_base_entry *e_from, *e_to;
49399ca880aSopenharmony_ci
49499ca880aSopenharmony_ci        assert(from != to);
49599ca880aSopenharmony_ci
49699ca880aSopenharmony_ci        e_from = bucket_at_virtual(h, swap, from);
49799ca880aSopenharmony_ci        e_to   = bucket_at_virtual(h, swap, to);
49899ca880aSopenharmony_ci
49999ca880aSopenharmony_ci        memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
50099ca880aSopenharmony_ci
50199ca880aSopenharmony_ci        if (h->type == HASHMAP_TYPE_ORDERED) {
50299ca880aSopenharmony_ci                OrderedHashmap *lh = (OrderedHashmap*) h;
50399ca880aSopenharmony_ci                struct ordered_hashmap_entry *le, *le_to;
50499ca880aSopenharmony_ci
50599ca880aSopenharmony_ci                le_to = (struct ordered_hashmap_entry*) e_to;
50699ca880aSopenharmony_ci
50799ca880aSopenharmony_ci                if (le_to->iterate_next != IDX_NIL) {
50899ca880aSopenharmony_ci                        le = (struct ordered_hashmap_entry*)
50999ca880aSopenharmony_ci                             bucket_at_virtual(h, swap, le_to->iterate_next);
51099ca880aSopenharmony_ci                        le->iterate_previous = to;
51199ca880aSopenharmony_ci                }
51299ca880aSopenharmony_ci
51399ca880aSopenharmony_ci                if (le_to->iterate_previous != IDX_NIL) {
51499ca880aSopenharmony_ci                        le = (struct ordered_hashmap_entry*)
51599ca880aSopenharmony_ci                             bucket_at_virtual(h, swap, le_to->iterate_previous);
51699ca880aSopenharmony_ci                        le->iterate_next = to;
51799ca880aSopenharmony_ci                }
51899ca880aSopenharmony_ci
51999ca880aSopenharmony_ci                if (lh->iterate_list_head == from)
52099ca880aSopenharmony_ci                        lh->iterate_list_head = to;
52199ca880aSopenharmony_ci                if (lh->iterate_list_tail == from)
52299ca880aSopenharmony_ci                        lh->iterate_list_tail = to;
52399ca880aSopenharmony_ci        }
52499ca880aSopenharmony_ci}
52599ca880aSopenharmony_ci
52699ca880aSopenharmony_cistatic unsigned next_idx(HashmapBase *h, unsigned idx) {
52799ca880aSopenharmony_ci        return (idx + 1U) % n_buckets(h);
52899ca880aSopenharmony_ci}
52999ca880aSopenharmony_ci
53099ca880aSopenharmony_cistatic unsigned prev_idx(HashmapBase *h, unsigned idx) {
53199ca880aSopenharmony_ci        return (n_buckets(h) + idx - 1U) % n_buckets(h);
53299ca880aSopenharmony_ci}
53399ca880aSopenharmony_ci
53499ca880aSopenharmony_cistatic void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
53599ca880aSopenharmony_ci        switch (h->type) {
53699ca880aSopenharmony_ci
53799ca880aSopenharmony_ci        case HASHMAP_TYPE_PLAIN:
53899ca880aSopenharmony_ci        case HASHMAP_TYPE_ORDERED:
53999ca880aSopenharmony_ci                return ((struct plain_hashmap_entry*)e)->value;
54099ca880aSopenharmony_ci
54199ca880aSopenharmony_ci        case HASHMAP_TYPE_SET:
54299ca880aSopenharmony_ci                return (void*) e->key;
54399ca880aSopenharmony_ci
54499ca880aSopenharmony_ci        default:
54599ca880aSopenharmony_ci                assert_not_reached("Unknown hashmap type");
54699ca880aSopenharmony_ci        }
54799ca880aSopenharmony_ci}
54899ca880aSopenharmony_ci
54999ca880aSopenharmony_cistatic void base_remove_entry(HashmapBase *h, unsigned idx) {
55099ca880aSopenharmony_ci        unsigned left, right, prev, dib;
55199ca880aSopenharmony_ci        dib_raw_t raw_dib, *dibs;
55299ca880aSopenharmony_ci
55399ca880aSopenharmony_ci        dibs = dib_raw_ptr(h);
55499ca880aSopenharmony_ci        assert(dibs[idx] != DIB_RAW_FREE);
55599ca880aSopenharmony_ci
55699ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
55799ca880aSopenharmony_ci        h->debug.rem_count++;
55899ca880aSopenharmony_ci        h->debug.last_rem_idx = idx;
55999ca880aSopenharmony_ci#endif
56099ca880aSopenharmony_ci
56199ca880aSopenharmony_ci        left = idx;
56299ca880aSopenharmony_ci        /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
56399ca880aSopenharmony_ci        for (right = next_idx(h, left); ; right = next_idx(h, right)) {
56499ca880aSopenharmony_ci                raw_dib = dibs[right];
56599ca880aSopenharmony_ci                if (raw_dib == 0 || raw_dib == DIB_RAW_FREE)
56699ca880aSopenharmony_ci                        break;
56799ca880aSopenharmony_ci
56899ca880aSopenharmony_ci                /* The buckets are not supposed to be all occupied and with DIB > 0.
56999ca880aSopenharmony_ci                 * That would mean we could make everyone better off by shifting them
57099ca880aSopenharmony_ci                 * backward. This scenario is impossible. */
57199ca880aSopenharmony_ci                assert(left != right);
57299ca880aSopenharmony_ci        }
57399ca880aSopenharmony_ci
57499ca880aSopenharmony_ci        if (h->type == HASHMAP_TYPE_ORDERED) {
57599ca880aSopenharmony_ci                OrderedHashmap *lh = (OrderedHashmap*) h;
57699ca880aSopenharmony_ci                struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
57799ca880aSopenharmony_ci
57899ca880aSopenharmony_ci                if (le->iterate_next != IDX_NIL)
57999ca880aSopenharmony_ci                        ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
58099ca880aSopenharmony_ci                else
58199ca880aSopenharmony_ci                        lh->iterate_list_tail = le->iterate_previous;
58299ca880aSopenharmony_ci
58399ca880aSopenharmony_ci                if (le->iterate_previous != IDX_NIL)
58499ca880aSopenharmony_ci                        ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
58599ca880aSopenharmony_ci                else
58699ca880aSopenharmony_ci                        lh->iterate_list_head = le->iterate_next;
58799ca880aSopenharmony_ci        }
58899ca880aSopenharmony_ci
58999ca880aSopenharmony_ci        /* Now shift all buckets in the interval (left, right) one step backwards */
59099ca880aSopenharmony_ci        for (prev = left, left = next_idx(h, left); left != right;
59199ca880aSopenharmony_ci             prev = left, left = next_idx(h, left)) {
59299ca880aSopenharmony_ci                dib = bucket_calculate_dib(h, left, dibs[left]);
59399ca880aSopenharmony_ci                assert(dib != 0);
59499ca880aSopenharmony_ci                bucket_move_entry(h, NULL, left, prev);
59599ca880aSopenharmony_ci                bucket_set_dib(h, prev, dib - 1);
59699ca880aSopenharmony_ci        }
59799ca880aSopenharmony_ci
59899ca880aSopenharmony_ci        bucket_mark_free(h, prev);
59999ca880aSopenharmony_ci        n_entries_dec(h);
60099ca880aSopenharmony_ci}
60199ca880aSopenharmony_ci#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
60299ca880aSopenharmony_ci
60399ca880aSopenharmony_cistatic unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
60499ca880aSopenharmony_ci        struct ordered_hashmap_entry *e;
60599ca880aSopenharmony_ci        unsigned idx;
60699ca880aSopenharmony_ci
60799ca880aSopenharmony_ci        assert(h);
60899ca880aSopenharmony_ci        assert(i);
60999ca880aSopenharmony_ci
61099ca880aSopenharmony_ci        if (i->idx == IDX_NIL)
61199ca880aSopenharmony_ci                goto at_end;
61299ca880aSopenharmony_ci
61399ca880aSopenharmony_ci        if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
61499ca880aSopenharmony_ci                goto at_end;
61599ca880aSopenharmony_ci
61699ca880aSopenharmony_ci        if (i->idx == IDX_FIRST) {
61799ca880aSopenharmony_ci                idx = h->iterate_list_head;
61899ca880aSopenharmony_ci                e = ordered_bucket_at(h, idx);
61999ca880aSopenharmony_ci        } else {
62099ca880aSopenharmony_ci                idx = i->idx;
62199ca880aSopenharmony_ci                e = ordered_bucket_at(h, idx);
62299ca880aSopenharmony_ci                /*
62399ca880aSopenharmony_ci                 * We allow removing the current entry while iterating, but removal may cause
62499ca880aSopenharmony_ci                 * a backward shift. The next entry may thus move one bucket to the left.
62599ca880aSopenharmony_ci                 * To detect when it happens, we remember the key pointer of the entry we were
62699ca880aSopenharmony_ci                 * going to iterate next. If it does not match, there was a backward shift.
62799ca880aSopenharmony_ci                 */
62899ca880aSopenharmony_ci                if (e->p.b.key != i->next_key) {
62999ca880aSopenharmony_ci                        idx = prev_idx(HASHMAP_BASE(h), idx);
63099ca880aSopenharmony_ci                        e = ordered_bucket_at(h, idx);
63199ca880aSopenharmony_ci                }
63299ca880aSopenharmony_ci                assert(e->p.b.key == i->next_key);
63399ca880aSopenharmony_ci        }
63499ca880aSopenharmony_ci
63599ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
63699ca880aSopenharmony_ci        i->prev_idx = idx;
63799ca880aSopenharmony_ci#endif
63899ca880aSopenharmony_ci
63999ca880aSopenharmony_ci        if (e->iterate_next != IDX_NIL) {
64099ca880aSopenharmony_ci                struct ordered_hashmap_entry *n;
64199ca880aSopenharmony_ci                i->idx = e->iterate_next;
64299ca880aSopenharmony_ci                n = ordered_bucket_at(h, i->idx);
64399ca880aSopenharmony_ci                i->next_key = n->p.b.key;
64499ca880aSopenharmony_ci        } else
64599ca880aSopenharmony_ci                i->idx = IDX_NIL;
64699ca880aSopenharmony_ci
64799ca880aSopenharmony_ci        return idx;
64899ca880aSopenharmony_ci
64999ca880aSopenharmony_ciat_end:
65099ca880aSopenharmony_ci        i->idx = IDX_NIL;
65199ca880aSopenharmony_ci        return IDX_NIL;
65299ca880aSopenharmony_ci}
65399ca880aSopenharmony_ci
65499ca880aSopenharmony_cistatic unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
65599ca880aSopenharmony_ci        unsigned idx;
65699ca880aSopenharmony_ci
65799ca880aSopenharmony_ci        assert(h);
65899ca880aSopenharmony_ci        assert(i);
65999ca880aSopenharmony_ci
66099ca880aSopenharmony_ci        if (i->idx == IDX_NIL)
66199ca880aSopenharmony_ci                goto at_end;
66299ca880aSopenharmony_ci
66399ca880aSopenharmony_ci        if (i->idx == IDX_FIRST) {
66499ca880aSopenharmony_ci                /* fast forward to the first occupied bucket */
66599ca880aSopenharmony_ci                if (h->has_indirect) {
66699ca880aSopenharmony_ci                        i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
66799ca880aSopenharmony_ci                        h->indirect.idx_lowest_entry = i->idx;
66899ca880aSopenharmony_ci                } else
66999ca880aSopenharmony_ci                        i->idx = skip_free_buckets(h, 0);
67099ca880aSopenharmony_ci
67199ca880aSopenharmony_ci                if (i->idx == IDX_NIL)
67299ca880aSopenharmony_ci                        goto at_end;
67399ca880aSopenharmony_ci        } else {
67499ca880aSopenharmony_ci                struct hashmap_base_entry *e;
67599ca880aSopenharmony_ci
67699ca880aSopenharmony_ci                assert(i->idx > 0);
67799ca880aSopenharmony_ci
67899ca880aSopenharmony_ci                e = bucket_at(h, i->idx);
67999ca880aSopenharmony_ci                /*
68099ca880aSopenharmony_ci                 * We allow removing the current entry while iterating, but removal may cause
68199ca880aSopenharmony_ci                 * a backward shift. The next entry may thus move one bucket to the left.
68299ca880aSopenharmony_ci                 * To detect when it happens, we remember the key pointer of the entry we were
68399ca880aSopenharmony_ci                 * going to iterate next. If it does not match, there was a backward shift.
68499ca880aSopenharmony_ci                 */
68599ca880aSopenharmony_ci                if (e->key != i->next_key)
68699ca880aSopenharmony_ci                        e = bucket_at(h, --i->idx);
68799ca880aSopenharmony_ci
68899ca880aSopenharmony_ci                assert(e->key == i->next_key);
68999ca880aSopenharmony_ci        }
69099ca880aSopenharmony_ci
69199ca880aSopenharmony_ci        idx = i->idx;
69299ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
69399ca880aSopenharmony_ci        i->prev_idx = idx;
69499ca880aSopenharmony_ci#endif
69599ca880aSopenharmony_ci
69699ca880aSopenharmony_ci        i->idx = skip_free_buckets(h, i->idx + 1);
69799ca880aSopenharmony_ci        if (i->idx != IDX_NIL)
69899ca880aSopenharmony_ci                i->next_key = bucket_at(h, i->idx)->key;
69999ca880aSopenharmony_ci        else
70099ca880aSopenharmony_ci                i->idx = IDX_NIL;
70199ca880aSopenharmony_ci
70299ca880aSopenharmony_ci        return idx;
70399ca880aSopenharmony_ci
70499ca880aSopenharmony_ciat_end:
70599ca880aSopenharmony_ci        i->idx = IDX_NIL;
70699ca880aSopenharmony_ci        return IDX_NIL;
70799ca880aSopenharmony_ci}
70899ca880aSopenharmony_ci
70999ca880aSopenharmony_cistatic unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
71099ca880aSopenharmony_ci        if (!h) {
71199ca880aSopenharmony_ci                i->idx = IDX_NIL;
71299ca880aSopenharmony_ci                return IDX_NIL;
71399ca880aSopenharmony_ci        }
71499ca880aSopenharmony_ci
71599ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
71699ca880aSopenharmony_ci        if (i->idx == IDX_FIRST) {
71799ca880aSopenharmony_ci                i->put_count = h->debug.put_count;
71899ca880aSopenharmony_ci                i->rem_count = h->debug.rem_count;
71999ca880aSopenharmony_ci        } else {
72099ca880aSopenharmony_ci                /* While iterating, must not add any new entries */
72199ca880aSopenharmony_ci                assert(i->put_count == h->debug.put_count);
72299ca880aSopenharmony_ci                /* ... or remove entries other than the current one */
72399ca880aSopenharmony_ci                assert(i->rem_count == h->debug.rem_count ||
72499ca880aSopenharmony_ci                       (i->rem_count == h->debug.rem_count - 1 &&
72599ca880aSopenharmony_ci                        i->prev_idx == h->debug.last_rem_idx));
72699ca880aSopenharmony_ci                /* Reset our removals counter */
72799ca880aSopenharmony_ci                i->rem_count = h->debug.rem_count;
72899ca880aSopenharmony_ci        }
72999ca880aSopenharmony_ci#endif
73099ca880aSopenharmony_ci
73199ca880aSopenharmony_ci        return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
73299ca880aSopenharmony_ci                                               : hashmap_iterate_in_internal_order(h, i);
73399ca880aSopenharmony_ci}
73499ca880aSopenharmony_ci
73599ca880aSopenharmony_civoid *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key) {
73699ca880aSopenharmony_ci        struct hashmap_base_entry *e;
73799ca880aSopenharmony_ci        void *data;
73899ca880aSopenharmony_ci        unsigned idx;
73999ca880aSopenharmony_ci
74099ca880aSopenharmony_ci        idx = hashmap_iterate_entry(h, i);
74199ca880aSopenharmony_ci        if (idx == IDX_NIL) {
74299ca880aSopenharmony_ci                if (key)
74399ca880aSopenharmony_ci                        *key = NULL;
74499ca880aSopenharmony_ci
74599ca880aSopenharmony_ci                return NULL;
74699ca880aSopenharmony_ci        }
74799ca880aSopenharmony_ci
74899ca880aSopenharmony_ci        e = bucket_at(h, idx);
74999ca880aSopenharmony_ci        data = entry_value(h, e);
75099ca880aSopenharmony_ci        if (key)
75199ca880aSopenharmony_ci                *key = e->key;
75299ca880aSopenharmony_ci
75399ca880aSopenharmony_ci        return data;
75499ca880aSopenharmony_ci}
75599ca880aSopenharmony_ci
75699ca880aSopenharmony_civoid *set_iterate(Set *s, Iterator *i) {
75799ca880aSopenharmony_ci        return internal_hashmap_iterate(HASHMAP_BASE(s), i, NULL);
75899ca880aSopenharmony_ci}
75999ca880aSopenharmony_ci
76099ca880aSopenharmony_ci#define HASHMAP_FOREACH_IDX(idx, h, i) \
76199ca880aSopenharmony_ci        for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
76299ca880aSopenharmony_ci             (idx != IDX_NIL); \
76399ca880aSopenharmony_ci             (idx) = hashmap_iterate_entry((h), &(i)))
76499ca880aSopenharmony_ci
76599ca880aSopenharmony_cistatic void reset_direct_storage(HashmapBase *h) {
76699ca880aSopenharmony_ci        const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
76799ca880aSopenharmony_ci        void *p;
76899ca880aSopenharmony_ci
76999ca880aSopenharmony_ci        assert(!h->has_indirect);
77099ca880aSopenharmony_ci
77199ca880aSopenharmony_ci        p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
77299ca880aSopenharmony_ci        memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
77399ca880aSopenharmony_ci}
77499ca880aSopenharmony_ci
77599ca880aSopenharmony_cistatic struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
77699ca880aSopenharmony_ci        HashmapBase *h;
77799ca880aSopenharmony_ci        const struct hashmap_type_info *hi = &hashmap_type_info[type];
77899ca880aSopenharmony_ci        bool use_pool;
77999ca880aSopenharmony_ci
78099ca880aSopenharmony_ci        use_pool = is_main_thread();
78199ca880aSopenharmony_ci
78299ca880aSopenharmony_ci        h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
78399ca880aSopenharmony_ci
78499ca880aSopenharmony_ci        if (!h)
78599ca880aSopenharmony_ci                return NULL;
78699ca880aSopenharmony_ci
78799ca880aSopenharmony_ci        h->type = type;
78899ca880aSopenharmony_ci        h->from_pool = use_pool;
78999ca880aSopenharmony_ci        h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
79099ca880aSopenharmony_ci
79199ca880aSopenharmony_ci        if (type == HASHMAP_TYPE_ORDERED) {
79299ca880aSopenharmony_ci                OrderedHashmap *lh = (OrderedHashmap*)h;
79399ca880aSopenharmony_ci                lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
79499ca880aSopenharmony_ci        }
79599ca880aSopenharmony_ci
79699ca880aSopenharmony_ci        reset_direct_storage(h);
79799ca880aSopenharmony_ci
79899ca880aSopenharmony_ci        if (!shared_hash_key_initialized) {
79999ca880aSopenharmony_ci                random_bytes(shared_hash_key, sizeof(shared_hash_key));
80099ca880aSopenharmony_ci                shared_hash_key_initialized= true;
80199ca880aSopenharmony_ci        }
80299ca880aSopenharmony_ci
80399ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
80499ca880aSopenharmony_ci        LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
80599ca880aSopenharmony_ci        h->debug.func = func;
80699ca880aSopenharmony_ci        h->debug.file = file;
80799ca880aSopenharmony_ci        h->debug.line = line;
80899ca880aSopenharmony_ci#endif
80999ca880aSopenharmony_ci
81099ca880aSopenharmony_ci        return h;
81199ca880aSopenharmony_ci}
81299ca880aSopenharmony_ci
81399ca880aSopenharmony_ciHashmap *internal_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
81499ca880aSopenharmony_ci        return (Hashmap*)        hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
81599ca880aSopenharmony_ci}
81699ca880aSopenharmony_ci
81799ca880aSopenharmony_ciOrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
81899ca880aSopenharmony_ci        return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
81999ca880aSopenharmony_ci}
82099ca880aSopenharmony_ci
82199ca880aSopenharmony_ciSet *internal_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
82299ca880aSopenharmony_ci        return (Set*)            hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
82399ca880aSopenharmony_ci}
82499ca880aSopenharmony_ci
82599ca880aSopenharmony_cistatic int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
82699ca880aSopenharmony_ci                                         enum HashmapType type HASHMAP_DEBUG_PARAMS) {
82799ca880aSopenharmony_ci        HashmapBase *q;
82899ca880aSopenharmony_ci
82999ca880aSopenharmony_ci        assert(h);
83099ca880aSopenharmony_ci
83199ca880aSopenharmony_ci        if (*h)
83299ca880aSopenharmony_ci                return 0;
83399ca880aSopenharmony_ci
83499ca880aSopenharmony_ci        q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
83599ca880aSopenharmony_ci        if (!q)
83699ca880aSopenharmony_ci                return -ENOMEM;
83799ca880aSopenharmony_ci
83899ca880aSopenharmony_ci        *h = q;
83999ca880aSopenharmony_ci        return 0;
84099ca880aSopenharmony_ci}
84199ca880aSopenharmony_ci
84299ca880aSopenharmony_ciint internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
84399ca880aSopenharmony_ci        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
84499ca880aSopenharmony_ci}
84599ca880aSopenharmony_ci
84699ca880aSopenharmony_ciint internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
84799ca880aSopenharmony_ci        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
84899ca880aSopenharmony_ci}
84999ca880aSopenharmony_ci
85099ca880aSopenharmony_ciint internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
85199ca880aSopenharmony_ci        return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
85299ca880aSopenharmony_ci}
85399ca880aSopenharmony_ci
85499ca880aSopenharmony_cistatic void hashmap_free_no_clear(HashmapBase *h) {
85599ca880aSopenharmony_ci        assert(!h->has_indirect);
85699ca880aSopenharmony_ci        assert(!h->n_direct_entries);
85799ca880aSopenharmony_ci
85899ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
85999ca880aSopenharmony_ci        LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
86099ca880aSopenharmony_ci#endif
86199ca880aSopenharmony_ci
86299ca880aSopenharmony_ci        if (h->from_pool)
86399ca880aSopenharmony_ci                mempool_free_tile(hashmap_type_info[h->type].mempool, h);
86499ca880aSopenharmony_ci        else
86599ca880aSopenharmony_ci                free(h);
86699ca880aSopenharmony_ci}
86799ca880aSopenharmony_ci
86899ca880aSopenharmony_ciHashmapBase *internal_hashmap_free(HashmapBase *h) {
86999ca880aSopenharmony_ci
87099ca880aSopenharmony_ci        /* Free the hashmap, but nothing in it */
87199ca880aSopenharmony_ci
87299ca880aSopenharmony_ci        if (h) {
87399ca880aSopenharmony_ci                internal_hashmap_clear(h);
87499ca880aSopenharmony_ci                hashmap_free_no_clear(h);
87599ca880aSopenharmony_ci        }
87699ca880aSopenharmony_ci
87799ca880aSopenharmony_ci        return NULL;
87899ca880aSopenharmony_ci}
87999ca880aSopenharmony_ci
88099ca880aSopenharmony_ciHashmapBase *internal_hashmap_free_free(HashmapBase *h) {
88199ca880aSopenharmony_ci
88299ca880aSopenharmony_ci        /* Free the hashmap and all data objects in it, but not the
88399ca880aSopenharmony_ci         * keys */
88499ca880aSopenharmony_ci
88599ca880aSopenharmony_ci        if (h) {
88699ca880aSopenharmony_ci                internal_hashmap_clear_free(h);
88799ca880aSopenharmony_ci                hashmap_free_no_clear(h);
88899ca880aSopenharmony_ci        }
88999ca880aSopenharmony_ci
89099ca880aSopenharmony_ci        return NULL;
89199ca880aSopenharmony_ci}
89299ca880aSopenharmony_ci
89399ca880aSopenharmony_ciHashmap *hashmap_free_free_free(Hashmap *h) {
89499ca880aSopenharmony_ci
89599ca880aSopenharmony_ci        /* Free the hashmap and all data and key objects in it */
89699ca880aSopenharmony_ci
89799ca880aSopenharmony_ci        if (h) {
89899ca880aSopenharmony_ci                hashmap_clear_free_free(h);
89999ca880aSopenharmony_ci                hashmap_free_no_clear(HASHMAP_BASE(h));
90099ca880aSopenharmony_ci        }
90199ca880aSopenharmony_ci
90299ca880aSopenharmony_ci        return NULL;
90399ca880aSopenharmony_ci}
90499ca880aSopenharmony_ci
90599ca880aSopenharmony_civoid internal_hashmap_clear(HashmapBase *h) {
90699ca880aSopenharmony_ci        if (!h)
90799ca880aSopenharmony_ci                return;
90899ca880aSopenharmony_ci
90999ca880aSopenharmony_ci        if (h->has_indirect) {
91099ca880aSopenharmony_ci                free(h->indirect.storage);
91199ca880aSopenharmony_ci                h->has_indirect = false;
91299ca880aSopenharmony_ci        }
91399ca880aSopenharmony_ci
91499ca880aSopenharmony_ci        h->n_direct_entries = 0;
91599ca880aSopenharmony_ci        reset_direct_storage(h);
91699ca880aSopenharmony_ci
91799ca880aSopenharmony_ci        if (h->type == HASHMAP_TYPE_ORDERED) {
91899ca880aSopenharmony_ci                OrderedHashmap *lh = (OrderedHashmap*) h;
91999ca880aSopenharmony_ci                lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
92099ca880aSopenharmony_ci        }
92199ca880aSopenharmony_ci}
92299ca880aSopenharmony_ci
92399ca880aSopenharmony_civoid internal_hashmap_clear_free(HashmapBase *h) {
92499ca880aSopenharmony_ci        unsigned idx;
92599ca880aSopenharmony_ci
92699ca880aSopenharmony_ci        if (!h)
92799ca880aSopenharmony_ci                return;
92899ca880aSopenharmony_ci
92999ca880aSopenharmony_ci        for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
93099ca880aSopenharmony_ci             idx = skip_free_buckets(h, idx + 1))
93199ca880aSopenharmony_ci                free(entry_value(h, bucket_at(h, idx)));
93299ca880aSopenharmony_ci
93399ca880aSopenharmony_ci        internal_hashmap_clear(h);
93499ca880aSopenharmony_ci}
93599ca880aSopenharmony_ci
93699ca880aSopenharmony_civoid hashmap_clear_free_free(Hashmap *h) {
93799ca880aSopenharmony_ci        unsigned idx;
93899ca880aSopenharmony_ci
93999ca880aSopenharmony_ci        if (!h)
94099ca880aSopenharmony_ci                return;
94199ca880aSopenharmony_ci
94299ca880aSopenharmony_ci        for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
94399ca880aSopenharmony_ci             idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
94499ca880aSopenharmony_ci                struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
94599ca880aSopenharmony_ci                free((void*)e->b.key);
94699ca880aSopenharmony_ci                free(e->value);
94799ca880aSopenharmony_ci        }
94899ca880aSopenharmony_ci
94999ca880aSopenharmony_ci        internal_hashmap_clear(HASHMAP_BASE(h));
95099ca880aSopenharmony_ci}
95199ca880aSopenharmony_ci
95299ca880aSopenharmony_cistatic int resize_buckets(HashmapBase *h, unsigned entries_add);
95399ca880aSopenharmony_ci
95499ca880aSopenharmony_ci/*
95599ca880aSopenharmony_ci * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
95699ca880aSopenharmony_ci * Performs Robin Hood swaps as it goes. The entry to put must be placed
95799ca880aSopenharmony_ci * by the caller into swap slot IDX_PUT.
95899ca880aSopenharmony_ci * If used for in-place resizing, may leave a displaced entry in swap slot
95999ca880aSopenharmony_ci * IDX_PUT. Caller must rehash it next.
96099ca880aSopenharmony_ci * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
96199ca880aSopenharmony_ci *          false otherwise.
96299ca880aSopenharmony_ci */
96399ca880aSopenharmony_cistatic bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
96499ca880aSopenharmony_ci                                   struct swap_entries *swap) {
96599ca880aSopenharmony_ci        dib_raw_t raw_dib, *dibs;
96699ca880aSopenharmony_ci        unsigned dib, distance;
96799ca880aSopenharmony_ci
96899ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
96999ca880aSopenharmony_ci        h->debug.put_count++;
97099ca880aSopenharmony_ci#endif
97199ca880aSopenharmony_ci
97299ca880aSopenharmony_ci        dibs = dib_raw_ptr(h);
97399ca880aSopenharmony_ci
97499ca880aSopenharmony_ci        for (distance = 0; ; distance++) {
97599ca880aSopenharmony_ci                raw_dib = dibs[idx];
97699ca880aSopenharmony_ci                if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) {
97799ca880aSopenharmony_ci                        if (raw_dib == DIB_RAW_REHASH)
97899ca880aSopenharmony_ci                                bucket_move_entry(h, swap, idx, IDX_TMP);
97999ca880aSopenharmony_ci
98099ca880aSopenharmony_ci                        if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
98199ca880aSopenharmony_ci                                h->indirect.idx_lowest_entry = idx;
98299ca880aSopenharmony_ci
98399ca880aSopenharmony_ci                        bucket_set_dib(h, idx, distance);
98499ca880aSopenharmony_ci                        bucket_move_entry(h, swap, IDX_PUT, idx);
98599ca880aSopenharmony_ci                        if (raw_dib == DIB_RAW_REHASH) {
98699ca880aSopenharmony_ci                                bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
98799ca880aSopenharmony_ci                                return true;
98899ca880aSopenharmony_ci                        }
98999ca880aSopenharmony_ci
99099ca880aSopenharmony_ci                        return false;
99199ca880aSopenharmony_ci                }
99299ca880aSopenharmony_ci
99399ca880aSopenharmony_ci                dib = bucket_calculate_dib(h, idx, raw_dib);
99499ca880aSopenharmony_ci
99599ca880aSopenharmony_ci                if (dib < distance) {
99699ca880aSopenharmony_ci                        /* Found a wealthier entry. Go Robin Hood! */
99799ca880aSopenharmony_ci                        bucket_set_dib(h, idx, distance);
99899ca880aSopenharmony_ci
99999ca880aSopenharmony_ci                        /* swap the entries */
100099ca880aSopenharmony_ci                        bucket_move_entry(h, swap, idx, IDX_TMP);
100199ca880aSopenharmony_ci                        bucket_move_entry(h, swap, IDX_PUT, idx);
100299ca880aSopenharmony_ci                        bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
100399ca880aSopenharmony_ci
100499ca880aSopenharmony_ci                        distance = dib;
100599ca880aSopenharmony_ci                }
100699ca880aSopenharmony_ci
100799ca880aSopenharmony_ci                idx = next_idx(h, idx);
100899ca880aSopenharmony_ci        }
100999ca880aSopenharmony_ci}
101099ca880aSopenharmony_ci
101199ca880aSopenharmony_ci/*
101299ca880aSopenharmony_ci * Puts an entry into a hashmap, boldly - no check whether key already exists.
101399ca880aSopenharmony_ci * The caller must place the entry (only its key and value, not link indexes)
101499ca880aSopenharmony_ci * in swap slot IDX_PUT.
101599ca880aSopenharmony_ci * Caller must ensure: the key does not exist yet in the hashmap.
101699ca880aSopenharmony_ci *                     that resize is not needed if !may_resize.
101799ca880aSopenharmony_ci * Returns: 1 if entry was put successfully.
101899ca880aSopenharmony_ci *          -ENOMEM if may_resize==true and resize failed with -ENOMEM.
101999ca880aSopenharmony_ci *          Cannot return -ENOMEM if !may_resize.
102099ca880aSopenharmony_ci */
102199ca880aSopenharmony_cistatic int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
102299ca880aSopenharmony_ci                                   struct swap_entries *swap, bool may_resize) {
102399ca880aSopenharmony_ci        struct ordered_hashmap_entry *new_entry;
102499ca880aSopenharmony_ci        int r;
102599ca880aSopenharmony_ci
102699ca880aSopenharmony_ci        assert(idx < n_buckets(h));
102799ca880aSopenharmony_ci
102899ca880aSopenharmony_ci        new_entry = bucket_at_swap(swap, IDX_PUT);
102999ca880aSopenharmony_ci
103099ca880aSopenharmony_ci        if (may_resize) {
103199ca880aSopenharmony_ci                r = resize_buckets(h, 1);
103299ca880aSopenharmony_ci                if (r < 0)
103399ca880aSopenharmony_ci                        return r;
103499ca880aSopenharmony_ci                if (r > 0)
103599ca880aSopenharmony_ci                        idx = bucket_hash(h, new_entry->p.b.key);
103699ca880aSopenharmony_ci        }
103799ca880aSopenharmony_ci        assert(n_entries(h) < n_buckets(h));
103899ca880aSopenharmony_ci
103999ca880aSopenharmony_ci        if (h->type == HASHMAP_TYPE_ORDERED) {
104099ca880aSopenharmony_ci                OrderedHashmap *lh = (OrderedHashmap*) h;
104199ca880aSopenharmony_ci
104299ca880aSopenharmony_ci                new_entry->iterate_next = IDX_NIL;
104399ca880aSopenharmony_ci                new_entry->iterate_previous = lh->iterate_list_tail;
104499ca880aSopenharmony_ci
104599ca880aSopenharmony_ci                if (lh->iterate_list_tail != IDX_NIL) {
104699ca880aSopenharmony_ci                        struct ordered_hashmap_entry *old_tail;
104799ca880aSopenharmony_ci
104899ca880aSopenharmony_ci                        old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
104999ca880aSopenharmony_ci                        assert(old_tail->iterate_next == IDX_NIL);
105099ca880aSopenharmony_ci                        old_tail->iterate_next = IDX_PUT;
105199ca880aSopenharmony_ci                }
105299ca880aSopenharmony_ci
105399ca880aSopenharmony_ci                lh->iterate_list_tail = IDX_PUT;
105499ca880aSopenharmony_ci                if (lh->iterate_list_head == IDX_NIL)
105599ca880aSopenharmony_ci                        lh->iterate_list_head = IDX_PUT;
105699ca880aSopenharmony_ci        }
105799ca880aSopenharmony_ci
105899ca880aSopenharmony_ci        assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
105999ca880aSopenharmony_ci
106099ca880aSopenharmony_ci        n_entries_inc(h);
106199ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
106299ca880aSopenharmony_ci        h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
106399ca880aSopenharmony_ci#endif
106499ca880aSopenharmony_ci
106599ca880aSopenharmony_ci        return 1;
106699ca880aSopenharmony_ci}
106799ca880aSopenharmony_ci#define hashmap_put_boldly(h, idx, swap, may_resize) \
106899ca880aSopenharmony_ci        hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
106999ca880aSopenharmony_ci
107099ca880aSopenharmony_ci/*
107199ca880aSopenharmony_ci * Returns 0 if resize is not needed.
107299ca880aSopenharmony_ci *         1 if successfully resized.
107399ca880aSopenharmony_ci *         -ENOMEM on allocation failure.
107499ca880aSopenharmony_ci */
107599ca880aSopenharmony_cistatic int resize_buckets(HashmapBase *h, unsigned entries_add) {
107699ca880aSopenharmony_ci        struct swap_entries swap;
107799ca880aSopenharmony_ci        char *new_storage;
107899ca880aSopenharmony_ci        dib_raw_t *old_dibs, *new_dibs;
107999ca880aSopenharmony_ci        const struct hashmap_type_info *hi;
108099ca880aSopenharmony_ci        unsigned idx, optimal_idx;
108199ca880aSopenharmony_ci        unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
108299ca880aSopenharmony_ci        uint8_t new_shift;
108399ca880aSopenharmony_ci        bool rehash_next;
108499ca880aSopenharmony_ci
108599ca880aSopenharmony_ci        assert(h);
108699ca880aSopenharmony_ci
108799ca880aSopenharmony_ci        hi = &hashmap_type_info[h->type];
108899ca880aSopenharmony_ci        new_n_entries = n_entries(h) + entries_add;
108999ca880aSopenharmony_ci
109099ca880aSopenharmony_ci        /* overflow? */
109199ca880aSopenharmony_ci        if (_unlikely_(new_n_entries < entries_add))
109299ca880aSopenharmony_ci                return -ENOMEM;
109399ca880aSopenharmony_ci
109499ca880aSopenharmony_ci        /* For direct storage we allow 100% load, because it's tiny. */
109599ca880aSopenharmony_ci        if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
109699ca880aSopenharmony_ci                return 0;
109799ca880aSopenharmony_ci
109899ca880aSopenharmony_ci        /*
109999ca880aSopenharmony_ci         * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
110099ca880aSopenharmony_ci         * From it follows: m = n + n/(INV_KEEP_FREE - 1)
110199ca880aSopenharmony_ci         */
110299ca880aSopenharmony_ci        new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
110399ca880aSopenharmony_ci        /* overflow? */
110499ca880aSopenharmony_ci        if (_unlikely_(new_n_buckets < new_n_entries))
110599ca880aSopenharmony_ci                return -ENOMEM;
110699ca880aSopenharmony_ci
110799ca880aSopenharmony_ci        if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
110899ca880aSopenharmony_ci                return -ENOMEM;
110999ca880aSopenharmony_ci
111099ca880aSopenharmony_ci        old_n_buckets = n_buckets(h);
111199ca880aSopenharmony_ci
111299ca880aSopenharmony_ci        if (_likely_(new_n_buckets <= old_n_buckets))
111399ca880aSopenharmony_ci                return 0;
111499ca880aSopenharmony_ci
111599ca880aSopenharmony_ci        new_shift = log2u_round_up(MAX(
111699ca880aSopenharmony_ci                        new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
111799ca880aSopenharmony_ci                        2 * sizeof(struct direct_storage)));
111899ca880aSopenharmony_ci
111999ca880aSopenharmony_ci        /* Realloc storage (buckets and DIB array). */
112099ca880aSopenharmony_ci        new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
112199ca880aSopenharmony_ci                              1U << new_shift);
112299ca880aSopenharmony_ci        if (!new_storage)
112399ca880aSopenharmony_ci                return -ENOMEM;
112499ca880aSopenharmony_ci
112599ca880aSopenharmony_ci        /* Must upgrade direct to indirect storage. */
112699ca880aSopenharmony_ci        if (!h->has_indirect) {
112799ca880aSopenharmony_ci                memcpy(new_storage, h->direct.storage,
112899ca880aSopenharmony_ci                       old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
112999ca880aSopenharmony_ci                h->indirect.n_entries = h->n_direct_entries;
113099ca880aSopenharmony_ci                h->indirect.idx_lowest_entry = 0;
113199ca880aSopenharmony_ci                h->n_direct_entries = 0;
113299ca880aSopenharmony_ci        }
113399ca880aSopenharmony_ci
113499ca880aSopenharmony_ci        /* Get a new hash key. If we've just upgraded to indirect storage,
113599ca880aSopenharmony_ci         * allow reusing a previously generated key. It's still a different key
113699ca880aSopenharmony_ci         * from the shared one that we used for direct storage. */
113799ca880aSopenharmony_ci        get_hash_key(h->indirect.hash_key, !h->has_indirect);
113899ca880aSopenharmony_ci
113999ca880aSopenharmony_ci        h->has_indirect = true;
114099ca880aSopenharmony_ci        h->indirect.storage = new_storage;
114199ca880aSopenharmony_ci        h->indirect.n_buckets = (1U << new_shift) /
114299ca880aSopenharmony_ci                                (hi->entry_size + sizeof(dib_raw_t));
114399ca880aSopenharmony_ci
114499ca880aSopenharmony_ci        old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
114599ca880aSopenharmony_ci        new_dibs = dib_raw_ptr(h);
114699ca880aSopenharmony_ci
114799ca880aSopenharmony_ci        /*
114899ca880aSopenharmony_ci         * Move the DIB array to the new place, replacing valid DIB values with
114999ca880aSopenharmony_ci         * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
115099ca880aSopenharmony_ci         * Note: Overlap is not possible, because we have at least doubled the
115199ca880aSopenharmony_ci         * number of buckets and dib_raw_t is smaller than any entry type.
115299ca880aSopenharmony_ci         */
115399ca880aSopenharmony_ci        for (idx = 0; idx < old_n_buckets; idx++) {
115499ca880aSopenharmony_ci                assert(old_dibs[idx] != DIB_RAW_REHASH);
115599ca880aSopenharmony_ci                new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
115699ca880aSopenharmony_ci                                                              : DIB_RAW_REHASH;
115799ca880aSopenharmony_ci        }
115899ca880aSopenharmony_ci
115999ca880aSopenharmony_ci        /* Zero the area of newly added entries (including the old DIB area) */
116099ca880aSopenharmony_ci        memset(bucket_at(h, old_n_buckets), 0,
116199ca880aSopenharmony_ci               (n_buckets(h) - old_n_buckets) * hi->entry_size);
116299ca880aSopenharmony_ci
116399ca880aSopenharmony_ci        /* The upper half of the new DIB array needs initialization */
116499ca880aSopenharmony_ci        memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
116599ca880aSopenharmony_ci               (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
116699ca880aSopenharmony_ci
116799ca880aSopenharmony_ci        /* Rehash entries that need it */
116899ca880aSopenharmony_ci        n_rehashed = 0;
116999ca880aSopenharmony_ci        for (idx = 0; idx < old_n_buckets; idx++) {
117099ca880aSopenharmony_ci                if (new_dibs[idx] != DIB_RAW_REHASH)
117199ca880aSopenharmony_ci                        continue;
117299ca880aSopenharmony_ci
117399ca880aSopenharmony_ci                optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
117499ca880aSopenharmony_ci
117599ca880aSopenharmony_ci                /*
117699ca880aSopenharmony_ci                 * Not much to do if by luck the entry hashes to its current
117799ca880aSopenharmony_ci                 * location. Just set its DIB.
117899ca880aSopenharmony_ci                 */
117999ca880aSopenharmony_ci                if (optimal_idx == idx) {
118099ca880aSopenharmony_ci                        new_dibs[idx] = 0;
118199ca880aSopenharmony_ci                        n_rehashed++;
118299ca880aSopenharmony_ci                        continue;
118399ca880aSopenharmony_ci                }
118499ca880aSopenharmony_ci
118599ca880aSopenharmony_ci                new_dibs[idx] = DIB_RAW_FREE;
118699ca880aSopenharmony_ci                bucket_move_entry(h, &swap, idx, IDX_PUT);
118799ca880aSopenharmony_ci                /* bucket_move_entry does not clear the source */
118899ca880aSopenharmony_ci                memset(bucket_at(h, idx), 0, hi->entry_size);
118999ca880aSopenharmony_ci
119099ca880aSopenharmony_ci                do {
119199ca880aSopenharmony_ci                        /*
119299ca880aSopenharmony_ci                         * Find the new bucket for the current entry. This may make
119399ca880aSopenharmony_ci                         * another entry homeless and load it into IDX_PUT.
119499ca880aSopenharmony_ci                         */
119599ca880aSopenharmony_ci                        rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
119699ca880aSopenharmony_ci                        n_rehashed++;
119799ca880aSopenharmony_ci
119899ca880aSopenharmony_ci                        /* Did the current entry displace another one? */
119999ca880aSopenharmony_ci                        if (rehash_next)
120099ca880aSopenharmony_ci                                optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
120199ca880aSopenharmony_ci                } while (rehash_next);
120299ca880aSopenharmony_ci        }
120399ca880aSopenharmony_ci
120499ca880aSopenharmony_ci        assert(n_rehashed == n_entries(h));
120599ca880aSopenharmony_ci
120699ca880aSopenharmony_ci        return 1;
120799ca880aSopenharmony_ci}
120899ca880aSopenharmony_ci
120999ca880aSopenharmony_ci/*
121099ca880aSopenharmony_ci * Finds an entry with a matching key
121199ca880aSopenharmony_ci * Returns: index of the found entry, or IDX_NIL if not found.
121299ca880aSopenharmony_ci */
121399ca880aSopenharmony_cistatic unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
121499ca880aSopenharmony_ci        struct hashmap_base_entry *e;
121599ca880aSopenharmony_ci        unsigned dib, distance;
121699ca880aSopenharmony_ci        dib_raw_t *dibs = dib_raw_ptr(h);
121799ca880aSopenharmony_ci
121899ca880aSopenharmony_ci        assert(idx < n_buckets(h));
121999ca880aSopenharmony_ci
122099ca880aSopenharmony_ci        for (distance = 0; ; distance++) {
122199ca880aSopenharmony_ci                if (dibs[idx] == DIB_RAW_FREE)
122299ca880aSopenharmony_ci                        return IDX_NIL;
122399ca880aSopenharmony_ci
122499ca880aSopenharmony_ci                dib = bucket_calculate_dib(h, idx, dibs[idx]);
122599ca880aSopenharmony_ci
122699ca880aSopenharmony_ci                if (dib < distance)
122799ca880aSopenharmony_ci                        return IDX_NIL;
122899ca880aSopenharmony_ci                if (dib == distance) {
122999ca880aSopenharmony_ci                        e = bucket_at(h, idx);
123099ca880aSopenharmony_ci                        if (h->hash_ops->compare(e->key, key) == 0)
123199ca880aSopenharmony_ci                                return idx;
123299ca880aSopenharmony_ci                }
123399ca880aSopenharmony_ci
123499ca880aSopenharmony_ci                idx = next_idx(h, idx);
123599ca880aSopenharmony_ci        }
123699ca880aSopenharmony_ci}
123799ca880aSopenharmony_ci#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
123899ca880aSopenharmony_ci
123999ca880aSopenharmony_ciint hashmap_put(Hashmap *h, const void *key, void *value) {
124099ca880aSopenharmony_ci        struct swap_entries swap;
124199ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
124299ca880aSopenharmony_ci        unsigned hash, idx;
124399ca880aSopenharmony_ci
124499ca880aSopenharmony_ci        assert(h);
124599ca880aSopenharmony_ci
124699ca880aSopenharmony_ci        hash = bucket_hash(h, key);
124799ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
124899ca880aSopenharmony_ci        if (idx != IDX_NIL) {
124999ca880aSopenharmony_ci                e = plain_bucket_at(h, idx);
125099ca880aSopenharmony_ci                if (e->value == value)
125199ca880aSopenharmony_ci                        return 0;
125299ca880aSopenharmony_ci                return -EEXIST;
125399ca880aSopenharmony_ci        }
125499ca880aSopenharmony_ci
125599ca880aSopenharmony_ci        e = &bucket_at_swap(&swap, IDX_PUT)->p;
125699ca880aSopenharmony_ci        e->b.key = key;
125799ca880aSopenharmony_ci        e->value = value;
125899ca880aSopenharmony_ci        return hashmap_put_boldly(h, hash, &swap, true);
125999ca880aSopenharmony_ci}
126099ca880aSopenharmony_ci
126199ca880aSopenharmony_ciint set_put(Set *s, const void *key) {
126299ca880aSopenharmony_ci        struct swap_entries swap;
126399ca880aSopenharmony_ci        struct hashmap_base_entry *e;
126499ca880aSopenharmony_ci        unsigned hash, idx;
126599ca880aSopenharmony_ci
126699ca880aSopenharmony_ci        assert(s);
126799ca880aSopenharmony_ci
126899ca880aSopenharmony_ci        hash = bucket_hash(s, key);
126999ca880aSopenharmony_ci        idx = bucket_scan(s, hash, key);
127099ca880aSopenharmony_ci        if (idx != IDX_NIL)
127199ca880aSopenharmony_ci                return 0;
127299ca880aSopenharmony_ci
127399ca880aSopenharmony_ci        e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
127499ca880aSopenharmony_ci        e->key = key;
127599ca880aSopenharmony_ci        return hashmap_put_boldly(s, hash, &swap, true);
127699ca880aSopenharmony_ci}
127799ca880aSopenharmony_ci
127899ca880aSopenharmony_ciint hashmap_replace(Hashmap *h, const void *key, void *value) {
127999ca880aSopenharmony_ci        struct swap_entries swap;
128099ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
128199ca880aSopenharmony_ci        unsigned hash, idx;
128299ca880aSopenharmony_ci
128399ca880aSopenharmony_ci        assert(h);
128499ca880aSopenharmony_ci
128599ca880aSopenharmony_ci        hash = bucket_hash(h, key);
128699ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
128799ca880aSopenharmony_ci        if (idx != IDX_NIL) {
128899ca880aSopenharmony_ci                e = plain_bucket_at(h, idx);
128999ca880aSopenharmony_ci#ifdef ENABLE_DEBUG_HASHMAP
129099ca880aSopenharmony_ci                /* Although the key is equal, the key pointer may have changed,
129199ca880aSopenharmony_ci                 * and this would break our assumption for iterating. So count
129299ca880aSopenharmony_ci                 * this operation as incompatible with iteration. */
129399ca880aSopenharmony_ci                if (e->b.key != key) {
129499ca880aSopenharmony_ci                        h->b.debug.put_count++;
129599ca880aSopenharmony_ci                        h->b.debug.rem_count++;
129699ca880aSopenharmony_ci                        h->b.debug.last_rem_idx = idx;
129799ca880aSopenharmony_ci                }
129899ca880aSopenharmony_ci#endif
129999ca880aSopenharmony_ci                e->b.key = key;
130099ca880aSopenharmony_ci                e->value = value;
130199ca880aSopenharmony_ci                return 0;
130299ca880aSopenharmony_ci        }
130399ca880aSopenharmony_ci
130499ca880aSopenharmony_ci        e = &bucket_at_swap(&swap, IDX_PUT)->p;
130599ca880aSopenharmony_ci        e->b.key = key;
130699ca880aSopenharmony_ci        e->value = value;
130799ca880aSopenharmony_ci        return hashmap_put_boldly(h, hash, &swap, true);
130899ca880aSopenharmony_ci}
130999ca880aSopenharmony_ci
131099ca880aSopenharmony_ciint hashmap_update(Hashmap *h, const void *key, void *value) {
131199ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
131299ca880aSopenharmony_ci        unsigned hash, idx;
131399ca880aSopenharmony_ci
131499ca880aSopenharmony_ci        assert(h);
131599ca880aSopenharmony_ci
131699ca880aSopenharmony_ci        hash = bucket_hash(h, key);
131799ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
131899ca880aSopenharmony_ci        if (idx == IDX_NIL)
131999ca880aSopenharmony_ci                return -ENOENT;
132099ca880aSopenharmony_ci
132199ca880aSopenharmony_ci        e = plain_bucket_at(h, idx);
132299ca880aSopenharmony_ci        e->value = value;
132399ca880aSopenharmony_ci        return 0;
132499ca880aSopenharmony_ci}
132599ca880aSopenharmony_ci
132699ca880aSopenharmony_civoid *internal_hashmap_get(HashmapBase *h, const void *key) {
132799ca880aSopenharmony_ci        struct hashmap_base_entry *e;
132899ca880aSopenharmony_ci        unsigned hash, idx;
132999ca880aSopenharmony_ci
133099ca880aSopenharmony_ci        if (!h)
133199ca880aSopenharmony_ci                return NULL;
133299ca880aSopenharmony_ci
133399ca880aSopenharmony_ci        hash = bucket_hash(h, key);
133499ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
133599ca880aSopenharmony_ci        if (idx == IDX_NIL)
133699ca880aSopenharmony_ci                return NULL;
133799ca880aSopenharmony_ci
133899ca880aSopenharmony_ci        e = bucket_at(h, idx);
133999ca880aSopenharmony_ci        return entry_value(h, e);
134099ca880aSopenharmony_ci}
134199ca880aSopenharmony_ci
134299ca880aSopenharmony_civoid *hashmap_get2(Hashmap *h, const void *key, void **key2) {
134399ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
134499ca880aSopenharmony_ci        unsigned hash, idx;
134599ca880aSopenharmony_ci
134699ca880aSopenharmony_ci        if (!h)
134799ca880aSopenharmony_ci                return NULL;
134899ca880aSopenharmony_ci
134999ca880aSopenharmony_ci        hash = bucket_hash(h, key);
135099ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
135199ca880aSopenharmony_ci        if (idx == IDX_NIL)
135299ca880aSopenharmony_ci                return NULL;
135399ca880aSopenharmony_ci
135499ca880aSopenharmony_ci        e = plain_bucket_at(h, idx);
135599ca880aSopenharmony_ci        if (key2)
135699ca880aSopenharmony_ci                *key2 = (void*) e->b.key;
135799ca880aSopenharmony_ci
135899ca880aSopenharmony_ci        return e->value;
135999ca880aSopenharmony_ci}
136099ca880aSopenharmony_ci
136199ca880aSopenharmony_cibool internal_hashmap_contains(HashmapBase *h, const void *key) {
136299ca880aSopenharmony_ci        unsigned hash;
136399ca880aSopenharmony_ci
136499ca880aSopenharmony_ci        if (!h)
136599ca880aSopenharmony_ci                return false;
136699ca880aSopenharmony_ci
136799ca880aSopenharmony_ci        hash = bucket_hash(h, key);
136899ca880aSopenharmony_ci        return bucket_scan(h, hash, key) != IDX_NIL;
136999ca880aSopenharmony_ci}
137099ca880aSopenharmony_ci
137199ca880aSopenharmony_civoid *internal_hashmap_remove(HashmapBase *h, const void *key) {
137299ca880aSopenharmony_ci        struct hashmap_base_entry *e;
137399ca880aSopenharmony_ci        unsigned hash, idx;
137499ca880aSopenharmony_ci        void *data;
137599ca880aSopenharmony_ci
137699ca880aSopenharmony_ci        if (!h)
137799ca880aSopenharmony_ci                return NULL;
137899ca880aSopenharmony_ci
137999ca880aSopenharmony_ci        hash = bucket_hash(h, key);
138099ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
138199ca880aSopenharmony_ci        if (idx == IDX_NIL)
138299ca880aSopenharmony_ci                return NULL;
138399ca880aSopenharmony_ci
138499ca880aSopenharmony_ci        e = bucket_at(h, idx);
138599ca880aSopenharmony_ci        data = entry_value(h, e);
138699ca880aSopenharmony_ci        remove_entry(h, idx);
138799ca880aSopenharmony_ci
138899ca880aSopenharmony_ci        return data;
138999ca880aSopenharmony_ci}
139099ca880aSopenharmony_ci
139199ca880aSopenharmony_civoid *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
139299ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
139399ca880aSopenharmony_ci        unsigned hash, idx;
139499ca880aSopenharmony_ci        void *data;
139599ca880aSopenharmony_ci
139699ca880aSopenharmony_ci        if (!h) {
139799ca880aSopenharmony_ci                if (rkey)
139899ca880aSopenharmony_ci                        *rkey = NULL;
139999ca880aSopenharmony_ci                return NULL;
140099ca880aSopenharmony_ci        }
140199ca880aSopenharmony_ci
140299ca880aSopenharmony_ci        hash = bucket_hash(h, key);
140399ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
140499ca880aSopenharmony_ci        if (idx == IDX_NIL) {
140599ca880aSopenharmony_ci                if (rkey)
140699ca880aSopenharmony_ci                        *rkey = NULL;
140799ca880aSopenharmony_ci                return NULL;
140899ca880aSopenharmony_ci        }
140999ca880aSopenharmony_ci
141099ca880aSopenharmony_ci        e = plain_bucket_at(h, idx);
141199ca880aSopenharmony_ci        data = e->value;
141299ca880aSopenharmony_ci        if (rkey)
141399ca880aSopenharmony_ci                *rkey = (void*) e->b.key;
141499ca880aSopenharmony_ci
141599ca880aSopenharmony_ci        remove_entry(h, idx);
141699ca880aSopenharmony_ci
141799ca880aSopenharmony_ci        return data;
141899ca880aSopenharmony_ci}
141999ca880aSopenharmony_ci
142099ca880aSopenharmony_ciint hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
142199ca880aSopenharmony_ci        struct swap_entries swap;
142299ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
142399ca880aSopenharmony_ci        unsigned old_hash, new_hash, idx;
142499ca880aSopenharmony_ci
142599ca880aSopenharmony_ci        if (!h)
142699ca880aSopenharmony_ci                return -ENOENT;
142799ca880aSopenharmony_ci
142899ca880aSopenharmony_ci        old_hash = bucket_hash(h, old_key);
142999ca880aSopenharmony_ci        idx = bucket_scan(h, old_hash, old_key);
143099ca880aSopenharmony_ci        if (idx == IDX_NIL)
143199ca880aSopenharmony_ci                return -ENOENT;
143299ca880aSopenharmony_ci
143399ca880aSopenharmony_ci        new_hash = bucket_hash(h, new_key);
143499ca880aSopenharmony_ci        if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
143599ca880aSopenharmony_ci                return -EEXIST;
143699ca880aSopenharmony_ci
143799ca880aSopenharmony_ci        remove_entry(h, idx);
143899ca880aSopenharmony_ci
143999ca880aSopenharmony_ci        e = &bucket_at_swap(&swap, IDX_PUT)->p;
144099ca880aSopenharmony_ci        e->b.key = new_key;
144199ca880aSopenharmony_ci        e->value = value;
144299ca880aSopenharmony_ci        assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
144399ca880aSopenharmony_ci
144499ca880aSopenharmony_ci        return 0;
144599ca880aSopenharmony_ci}
144699ca880aSopenharmony_ci
144799ca880aSopenharmony_ciint set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
144899ca880aSopenharmony_ci        struct swap_entries swap;
144999ca880aSopenharmony_ci        struct hashmap_base_entry *e;
145099ca880aSopenharmony_ci        unsigned old_hash, new_hash, idx;
145199ca880aSopenharmony_ci
145299ca880aSopenharmony_ci        if (!s)
145399ca880aSopenharmony_ci                return -ENOENT;
145499ca880aSopenharmony_ci
145599ca880aSopenharmony_ci        old_hash = bucket_hash(s, old_key);
145699ca880aSopenharmony_ci        idx = bucket_scan(s, old_hash, old_key);
145799ca880aSopenharmony_ci        if (idx == IDX_NIL)
145899ca880aSopenharmony_ci                return -ENOENT;
145999ca880aSopenharmony_ci
146099ca880aSopenharmony_ci        new_hash = bucket_hash(s, new_key);
146199ca880aSopenharmony_ci        if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
146299ca880aSopenharmony_ci                return -EEXIST;
146399ca880aSopenharmony_ci
146499ca880aSopenharmony_ci        remove_entry(s, idx);
146599ca880aSopenharmony_ci
146699ca880aSopenharmony_ci        e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
146799ca880aSopenharmony_ci        e->key = new_key;
146899ca880aSopenharmony_ci        assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
146999ca880aSopenharmony_ci
147099ca880aSopenharmony_ci        return 0;
147199ca880aSopenharmony_ci}
147299ca880aSopenharmony_ci
147399ca880aSopenharmony_ciint hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
147499ca880aSopenharmony_ci        struct swap_entries swap;
147599ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
147699ca880aSopenharmony_ci        unsigned old_hash, new_hash, idx_old, idx_new;
147799ca880aSopenharmony_ci
147899ca880aSopenharmony_ci        if (!h)
147999ca880aSopenharmony_ci                return -ENOENT;
148099ca880aSopenharmony_ci
148199ca880aSopenharmony_ci        old_hash = bucket_hash(h, old_key);
148299ca880aSopenharmony_ci        idx_old = bucket_scan(h, old_hash, old_key);
148399ca880aSopenharmony_ci        if (idx_old == IDX_NIL)
148499ca880aSopenharmony_ci                return -ENOENT;
148599ca880aSopenharmony_ci
148699ca880aSopenharmony_ci        old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
148799ca880aSopenharmony_ci
148899ca880aSopenharmony_ci        new_hash = bucket_hash(h, new_key);
148999ca880aSopenharmony_ci        idx_new = bucket_scan(h, new_hash, new_key);
149099ca880aSopenharmony_ci        if (idx_new != IDX_NIL)
149199ca880aSopenharmony_ci                if (idx_old != idx_new) {
149299ca880aSopenharmony_ci                        remove_entry(h, idx_new);
149399ca880aSopenharmony_ci                        /* Compensate for a possible backward shift. */
149499ca880aSopenharmony_ci                        if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
149599ca880aSopenharmony_ci                                idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
149699ca880aSopenharmony_ci                        assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
149799ca880aSopenharmony_ci                }
149899ca880aSopenharmony_ci
149999ca880aSopenharmony_ci        remove_entry(h, idx_old);
150099ca880aSopenharmony_ci
150199ca880aSopenharmony_ci        e = &bucket_at_swap(&swap, IDX_PUT)->p;
150299ca880aSopenharmony_ci        e->b.key = new_key;
150399ca880aSopenharmony_ci        e->value = value;
150499ca880aSopenharmony_ci        assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
150599ca880aSopenharmony_ci
150699ca880aSopenharmony_ci        return 0;
150799ca880aSopenharmony_ci}
150899ca880aSopenharmony_ci
150999ca880aSopenharmony_civoid *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
151099ca880aSopenharmony_ci        struct plain_hashmap_entry *e;
151199ca880aSopenharmony_ci        unsigned hash, idx;
151299ca880aSopenharmony_ci
151399ca880aSopenharmony_ci        if (!h)
151499ca880aSopenharmony_ci                return NULL;
151599ca880aSopenharmony_ci
151699ca880aSopenharmony_ci        hash = bucket_hash(h, key);
151799ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
151899ca880aSopenharmony_ci        if (idx == IDX_NIL)
151999ca880aSopenharmony_ci                return NULL;
152099ca880aSopenharmony_ci
152199ca880aSopenharmony_ci        e = plain_bucket_at(h, idx);
152299ca880aSopenharmony_ci        if (e->value != value)
152399ca880aSopenharmony_ci                return NULL;
152499ca880aSopenharmony_ci
152599ca880aSopenharmony_ci        remove_entry(h, idx);
152699ca880aSopenharmony_ci
152799ca880aSopenharmony_ci        return value;
152899ca880aSopenharmony_ci}
152999ca880aSopenharmony_ci
153099ca880aSopenharmony_cistatic unsigned find_first_entry(HashmapBase *h) {
153199ca880aSopenharmony_ci        Iterator i = ITERATOR_FIRST;
153299ca880aSopenharmony_ci
153399ca880aSopenharmony_ci        if (!h || !n_entries(h))
153499ca880aSopenharmony_ci                return IDX_NIL;
153599ca880aSopenharmony_ci
153699ca880aSopenharmony_ci        return hashmap_iterate_entry(h, &i);
153799ca880aSopenharmony_ci}
153899ca880aSopenharmony_ci
153999ca880aSopenharmony_civoid *internal_hashmap_first(HashmapBase *h) {
154099ca880aSopenharmony_ci        unsigned idx;
154199ca880aSopenharmony_ci
154299ca880aSopenharmony_ci        idx = find_first_entry(h);
154399ca880aSopenharmony_ci        if (idx == IDX_NIL)
154499ca880aSopenharmony_ci                return NULL;
154599ca880aSopenharmony_ci
154699ca880aSopenharmony_ci        return entry_value(h, bucket_at(h, idx));
154799ca880aSopenharmony_ci}
154899ca880aSopenharmony_ci
154999ca880aSopenharmony_civoid *internal_hashmap_first_key(HashmapBase *h) {
155099ca880aSopenharmony_ci        struct hashmap_base_entry *e;
155199ca880aSopenharmony_ci        unsigned idx;
155299ca880aSopenharmony_ci
155399ca880aSopenharmony_ci        idx = find_first_entry(h);
155499ca880aSopenharmony_ci        if (idx == IDX_NIL)
155599ca880aSopenharmony_ci                return NULL;
155699ca880aSopenharmony_ci
155799ca880aSopenharmony_ci        e = bucket_at(h, idx);
155899ca880aSopenharmony_ci        return (void*) e->key;
155999ca880aSopenharmony_ci}
156099ca880aSopenharmony_ci
156199ca880aSopenharmony_civoid *internal_hashmap_steal_first(HashmapBase *h) {
156299ca880aSopenharmony_ci        struct hashmap_base_entry *e;
156399ca880aSopenharmony_ci        void *data;
156499ca880aSopenharmony_ci        unsigned idx;
156599ca880aSopenharmony_ci
156699ca880aSopenharmony_ci        idx = find_first_entry(h);
156799ca880aSopenharmony_ci        if (idx == IDX_NIL)
156899ca880aSopenharmony_ci                return NULL;
156999ca880aSopenharmony_ci
157099ca880aSopenharmony_ci        e = bucket_at(h, idx);
157199ca880aSopenharmony_ci        data = entry_value(h, e);
157299ca880aSopenharmony_ci        remove_entry(h, idx);
157399ca880aSopenharmony_ci
157499ca880aSopenharmony_ci        return data;
157599ca880aSopenharmony_ci}
157699ca880aSopenharmony_ci
157799ca880aSopenharmony_civoid *internal_hashmap_steal_first_key(HashmapBase *h) {
157899ca880aSopenharmony_ci        struct hashmap_base_entry *e;
157999ca880aSopenharmony_ci        void *key;
158099ca880aSopenharmony_ci        unsigned idx;
158199ca880aSopenharmony_ci
158299ca880aSopenharmony_ci        idx = find_first_entry(h);
158399ca880aSopenharmony_ci        if (idx == IDX_NIL)
158499ca880aSopenharmony_ci                return NULL;
158599ca880aSopenharmony_ci
158699ca880aSopenharmony_ci        e = bucket_at(h, idx);
158799ca880aSopenharmony_ci        key = (void*) e->key;
158899ca880aSopenharmony_ci        remove_entry(h, idx);
158999ca880aSopenharmony_ci
159099ca880aSopenharmony_ci        return key;
159199ca880aSopenharmony_ci}
159299ca880aSopenharmony_ci
159399ca880aSopenharmony_ciunsigned internal_hashmap_size(HashmapBase *h) {
159499ca880aSopenharmony_ci
159599ca880aSopenharmony_ci        if (!h)
159699ca880aSopenharmony_ci                return 0;
159799ca880aSopenharmony_ci
159899ca880aSopenharmony_ci        return n_entries(h);
159999ca880aSopenharmony_ci}
160099ca880aSopenharmony_ci
160199ca880aSopenharmony_ciunsigned internal_hashmap_buckets(HashmapBase *h) {
160299ca880aSopenharmony_ci
160399ca880aSopenharmony_ci        if (!h)
160499ca880aSopenharmony_ci                return 0;
160599ca880aSopenharmony_ci
160699ca880aSopenharmony_ci        return n_buckets(h);
160799ca880aSopenharmony_ci}
160899ca880aSopenharmony_ci
160999ca880aSopenharmony_ciint internal_hashmap_merge(Hashmap *h, Hashmap *other) {
161099ca880aSopenharmony_ci        Iterator i;
161199ca880aSopenharmony_ci        unsigned idx;
161299ca880aSopenharmony_ci
161399ca880aSopenharmony_ci        assert(h);
161499ca880aSopenharmony_ci
161599ca880aSopenharmony_ci        HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
161699ca880aSopenharmony_ci                struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
161799ca880aSopenharmony_ci                int r;
161899ca880aSopenharmony_ci
161999ca880aSopenharmony_ci                r = hashmap_put(h, pe->b.key, pe->value);
162099ca880aSopenharmony_ci                if (r < 0 && r != -EEXIST)
162199ca880aSopenharmony_ci                        return r;
162299ca880aSopenharmony_ci        }
162399ca880aSopenharmony_ci
162499ca880aSopenharmony_ci        return 0;
162599ca880aSopenharmony_ci}
162699ca880aSopenharmony_ci
162799ca880aSopenharmony_ciint set_merge(Set *s, Set *other) {
162899ca880aSopenharmony_ci        Iterator i;
162999ca880aSopenharmony_ci        unsigned idx;
163099ca880aSopenharmony_ci
163199ca880aSopenharmony_ci        assert(s);
163299ca880aSopenharmony_ci
163399ca880aSopenharmony_ci        HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
163499ca880aSopenharmony_ci                struct set_entry *se = set_bucket_at(other, idx);
163599ca880aSopenharmony_ci                int r;
163699ca880aSopenharmony_ci
163799ca880aSopenharmony_ci                r = set_put(s, se->b.key);
163899ca880aSopenharmony_ci                if (r < 0)
163999ca880aSopenharmony_ci                        return r;
164099ca880aSopenharmony_ci        }
164199ca880aSopenharmony_ci
164299ca880aSopenharmony_ci        return 0;
164399ca880aSopenharmony_ci}
164499ca880aSopenharmony_ci
164599ca880aSopenharmony_ciint internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
164699ca880aSopenharmony_ci        int r;
164799ca880aSopenharmony_ci
164899ca880aSopenharmony_ci        assert(h);
164999ca880aSopenharmony_ci
165099ca880aSopenharmony_ci        r = resize_buckets(h, entries_add);
165199ca880aSopenharmony_ci        if (r < 0)
165299ca880aSopenharmony_ci                return r;
165399ca880aSopenharmony_ci
165499ca880aSopenharmony_ci        return 0;
165599ca880aSopenharmony_ci}
165699ca880aSopenharmony_ci
165799ca880aSopenharmony_ci/*
165899ca880aSopenharmony_ci * The same as hashmap_merge(), but every new item from other is moved to h.
165999ca880aSopenharmony_ci * Keys already in h are skipped and stay in other.
166099ca880aSopenharmony_ci * Returns: 0 on success.
166199ca880aSopenharmony_ci *          -ENOMEM on alloc failure, in which case no move has been done.
166299ca880aSopenharmony_ci */
166399ca880aSopenharmony_ciint internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
166499ca880aSopenharmony_ci        struct swap_entries swap;
166599ca880aSopenharmony_ci        struct hashmap_base_entry *e, *n;
166699ca880aSopenharmony_ci        Iterator i;
166799ca880aSopenharmony_ci        unsigned idx;
166899ca880aSopenharmony_ci        int r;
166999ca880aSopenharmony_ci
167099ca880aSopenharmony_ci        assert(h);
167199ca880aSopenharmony_ci
167299ca880aSopenharmony_ci        if (!other)
167399ca880aSopenharmony_ci                return 0;
167499ca880aSopenharmony_ci
167599ca880aSopenharmony_ci        assert(other->type == h->type);
167699ca880aSopenharmony_ci
167799ca880aSopenharmony_ci        /*
167899ca880aSopenharmony_ci         * This reserves buckets for the worst case, where none of other's
167999ca880aSopenharmony_ci         * entries are yet present in h. This is preferable to risking
168099ca880aSopenharmony_ci         * an allocation failure in the middle of the moving and having to
168199ca880aSopenharmony_ci         * rollback or return a partial result.
168299ca880aSopenharmony_ci         */
168399ca880aSopenharmony_ci        r = resize_buckets(h, n_entries(other));
168499ca880aSopenharmony_ci        if (r < 0)
168599ca880aSopenharmony_ci                return r;
168699ca880aSopenharmony_ci
168799ca880aSopenharmony_ci        HASHMAP_FOREACH_IDX(idx, other, i) {
168899ca880aSopenharmony_ci                unsigned h_hash;
168999ca880aSopenharmony_ci
169099ca880aSopenharmony_ci                e = bucket_at(other, idx);
169199ca880aSopenharmony_ci                h_hash = bucket_hash(h, e->key);
169299ca880aSopenharmony_ci                if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
169399ca880aSopenharmony_ci                        continue;
169499ca880aSopenharmony_ci
169599ca880aSopenharmony_ci                n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
169699ca880aSopenharmony_ci                n->key = e->key;
169799ca880aSopenharmony_ci                if (h->type != HASHMAP_TYPE_SET)
169899ca880aSopenharmony_ci                        ((struct plain_hashmap_entry*) n)->value =
169999ca880aSopenharmony_ci                                ((struct plain_hashmap_entry*) e)->value;
170099ca880aSopenharmony_ci                assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
170199ca880aSopenharmony_ci
170299ca880aSopenharmony_ci                remove_entry(other, idx);
170399ca880aSopenharmony_ci        }
170499ca880aSopenharmony_ci
170599ca880aSopenharmony_ci        return 0;
170699ca880aSopenharmony_ci}
170799ca880aSopenharmony_ci
170899ca880aSopenharmony_ciint internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
170999ca880aSopenharmony_ci        struct swap_entries swap;
171099ca880aSopenharmony_ci        unsigned h_hash, other_hash, idx;
171199ca880aSopenharmony_ci        struct hashmap_base_entry *e, *n;
171299ca880aSopenharmony_ci        int r;
171399ca880aSopenharmony_ci
171499ca880aSopenharmony_ci        assert(h);
171599ca880aSopenharmony_ci
171699ca880aSopenharmony_ci        h_hash = bucket_hash(h, key);
171799ca880aSopenharmony_ci        if (bucket_scan(h, h_hash, key) != IDX_NIL)
171899ca880aSopenharmony_ci                return -EEXIST;
171999ca880aSopenharmony_ci
172099ca880aSopenharmony_ci        if (!other)
172199ca880aSopenharmony_ci                return -ENOENT;
172299ca880aSopenharmony_ci
172399ca880aSopenharmony_ci        assert(other->type == h->type);
172499ca880aSopenharmony_ci
172599ca880aSopenharmony_ci        other_hash = bucket_hash(other, key);
172699ca880aSopenharmony_ci        idx = bucket_scan(other, other_hash, key);
172799ca880aSopenharmony_ci        if (idx == IDX_NIL)
172899ca880aSopenharmony_ci                return -ENOENT;
172999ca880aSopenharmony_ci
173099ca880aSopenharmony_ci        e = bucket_at(other, idx);
173199ca880aSopenharmony_ci
173299ca880aSopenharmony_ci        n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
173399ca880aSopenharmony_ci        n->key = e->key;
173499ca880aSopenharmony_ci        if (h->type != HASHMAP_TYPE_SET)
173599ca880aSopenharmony_ci                ((struct plain_hashmap_entry*) n)->value =
173699ca880aSopenharmony_ci                        ((struct plain_hashmap_entry*) e)->value;
173799ca880aSopenharmony_ci        r = hashmap_put_boldly(h, h_hash, &swap, true);
173899ca880aSopenharmony_ci        if (r < 0)
173999ca880aSopenharmony_ci                return r;
174099ca880aSopenharmony_ci
174199ca880aSopenharmony_ci        remove_entry(other, idx);
174299ca880aSopenharmony_ci        return 0;
174399ca880aSopenharmony_ci}
174499ca880aSopenharmony_ci
174599ca880aSopenharmony_ciHashmapBase *internal_hashmap_copy(HashmapBase *h) {
174699ca880aSopenharmony_ci        HashmapBase *copy;
174799ca880aSopenharmony_ci        int r;
174899ca880aSopenharmony_ci
174999ca880aSopenharmony_ci        assert(h);
175099ca880aSopenharmony_ci
175199ca880aSopenharmony_ci        copy = hashmap_base_new(h->hash_ops, h->type  HASHMAP_DEBUG_SRC_ARGS);
175299ca880aSopenharmony_ci        if (!copy)
175399ca880aSopenharmony_ci                return NULL;
175499ca880aSopenharmony_ci
175599ca880aSopenharmony_ci        switch (h->type) {
175699ca880aSopenharmony_ci        case HASHMAP_TYPE_PLAIN:
175799ca880aSopenharmony_ci        case HASHMAP_TYPE_ORDERED:
175899ca880aSopenharmony_ci                r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
175999ca880aSopenharmony_ci                break;
176099ca880aSopenharmony_ci        case HASHMAP_TYPE_SET:
176199ca880aSopenharmony_ci                r = set_merge((Set*)copy, (Set*)h);
176299ca880aSopenharmony_ci                break;
176399ca880aSopenharmony_ci        default:
176499ca880aSopenharmony_ci                assert_not_reached("Unknown hashmap type");
176599ca880aSopenharmony_ci        }
176699ca880aSopenharmony_ci
176799ca880aSopenharmony_ci        if (r < 0) {
176899ca880aSopenharmony_ci                internal_hashmap_free(copy);
176999ca880aSopenharmony_ci                return NULL;
177099ca880aSopenharmony_ci        }
177199ca880aSopenharmony_ci
177299ca880aSopenharmony_ci        return copy;
177399ca880aSopenharmony_ci}
177499ca880aSopenharmony_ci
177599ca880aSopenharmony_cichar **internal_hashmap_get_strv(HashmapBase *h) {
177699ca880aSopenharmony_ci        char **sv;
177799ca880aSopenharmony_ci        Iterator i;
177899ca880aSopenharmony_ci        unsigned idx, n;
177999ca880aSopenharmony_ci
178099ca880aSopenharmony_ci        sv = new(char*, n_entries(h)+1);
178199ca880aSopenharmony_ci        if (!sv)
178299ca880aSopenharmony_ci                return NULL;
178399ca880aSopenharmony_ci
178499ca880aSopenharmony_ci        n = 0;
178599ca880aSopenharmony_ci        HASHMAP_FOREACH_IDX(idx, h, i)
178699ca880aSopenharmony_ci                sv[n++] = entry_value(h, bucket_at(h, idx));
178799ca880aSopenharmony_ci        sv[n] = NULL;
178899ca880aSopenharmony_ci
178999ca880aSopenharmony_ci        return sv;
179099ca880aSopenharmony_ci}
179199ca880aSopenharmony_ci
179299ca880aSopenharmony_civoid *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
179399ca880aSopenharmony_ci        struct ordered_hashmap_entry *e;
179499ca880aSopenharmony_ci        unsigned hash, idx;
179599ca880aSopenharmony_ci
179699ca880aSopenharmony_ci        assert(key);
179799ca880aSopenharmony_ci
179899ca880aSopenharmony_ci        if (!h)
179999ca880aSopenharmony_ci                return NULL;
180099ca880aSopenharmony_ci
180199ca880aSopenharmony_ci        hash = bucket_hash(h, key);
180299ca880aSopenharmony_ci        idx = bucket_scan(h, hash, key);
180399ca880aSopenharmony_ci        if (idx == IDX_NIL)
180499ca880aSopenharmony_ci                return NULL;
180599ca880aSopenharmony_ci
180699ca880aSopenharmony_ci        e = ordered_bucket_at(h, idx);
180799ca880aSopenharmony_ci        if (e->iterate_next == IDX_NIL)
180899ca880aSopenharmony_ci                return NULL;
180999ca880aSopenharmony_ci        return ordered_bucket_at(h, e->iterate_next)->p.value;
181099ca880aSopenharmony_ci}
181199ca880aSopenharmony_ci
181299ca880aSopenharmony_ciint set_consume(Set *s, void *value) {
181399ca880aSopenharmony_ci        int r;
181499ca880aSopenharmony_ci
181599ca880aSopenharmony_ci        r = set_put(s, value);
181699ca880aSopenharmony_ci        if (r <= 0)
181799ca880aSopenharmony_ci                free(value);
181899ca880aSopenharmony_ci
181999ca880aSopenharmony_ci        return r;
182099ca880aSopenharmony_ci}
182199ca880aSopenharmony_ci
182299ca880aSopenharmony_ciint set_put_strdup(Set *s, const char *p) {
182399ca880aSopenharmony_ci        char *c;
182499ca880aSopenharmony_ci        int r;
182599ca880aSopenharmony_ci
182699ca880aSopenharmony_ci        assert(s);
182799ca880aSopenharmony_ci        assert(p);
182899ca880aSopenharmony_ci
182999ca880aSopenharmony_ci        c = strdup(p);
183099ca880aSopenharmony_ci        if (!c)
183199ca880aSopenharmony_ci                return -ENOMEM;
183299ca880aSopenharmony_ci
183399ca880aSopenharmony_ci        r = set_consume(s, c);
183499ca880aSopenharmony_ci        if (r == -EEXIST)
183599ca880aSopenharmony_ci                return 0;
183699ca880aSopenharmony_ci
183799ca880aSopenharmony_ci        return r;
183899ca880aSopenharmony_ci}
183999ca880aSopenharmony_ci
184099ca880aSopenharmony_ciint set_put_strdupv(Set *s, char **l) {
184199ca880aSopenharmony_ci        int n = 0, r;
184299ca880aSopenharmony_ci        char **i;
184399ca880aSopenharmony_ci
184499ca880aSopenharmony_ci        STRV_FOREACH(i, l) {
184599ca880aSopenharmony_ci                r = set_put_strdup(s, *i);
184699ca880aSopenharmony_ci                if (r < 0)
184799ca880aSopenharmony_ci                        return r;
184899ca880aSopenharmony_ci
184999ca880aSopenharmony_ci                n += r;
185099ca880aSopenharmony_ci        }
185199ca880aSopenharmony_ci
185299ca880aSopenharmony_ci        return n;
185399ca880aSopenharmony_ci}
1854