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