18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Generic non-thread safe hash map implementation. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2019 Facebook 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <stdint.h> 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <stdio.h> 118c2ecf20Sopenharmony_ci#include <errno.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include "hashmap.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* make sure libbpf doesn't use kernel-only integer typedefs */ 168c2ecf20Sopenharmony_ci#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* prevent accidental re-addition of reallocarray() */ 198c2ecf20Sopenharmony_ci#pragma GCC poison reallocarray 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* start with 4 buckets */ 228c2ecf20Sopenharmony_ci#define HASHMAP_MIN_CAP_BITS 2 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void hashmap_add_entry(struct hashmap_entry **pprev, 258c2ecf20Sopenharmony_ci struct hashmap_entry *entry) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci entry->next = *pprev; 288c2ecf20Sopenharmony_ci *pprev = entry; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void hashmap_del_entry(struct hashmap_entry **pprev, 328c2ecf20Sopenharmony_ci struct hashmap_entry *entry) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci *pprev = entry->next; 358c2ecf20Sopenharmony_ci entry->next = NULL; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_civoid hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn, 398c2ecf20Sopenharmony_ci hashmap_equal_fn equal_fn, void *ctx) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci map->hash_fn = hash_fn; 428c2ecf20Sopenharmony_ci map->equal_fn = equal_fn; 438c2ecf20Sopenharmony_ci map->ctx = ctx; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci map->buckets = NULL; 468c2ecf20Sopenharmony_ci map->cap = 0; 478c2ecf20Sopenharmony_ci map->cap_bits = 0; 488c2ecf20Sopenharmony_ci map->sz = 0; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct hashmap *hashmap__new(hashmap_hash_fn hash_fn, 528c2ecf20Sopenharmony_ci hashmap_equal_fn equal_fn, 538c2ecf20Sopenharmony_ci void *ctx) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct hashmap *map = malloc(sizeof(struct hashmap)); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (!map) 588c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 598c2ecf20Sopenharmony_ci hashmap__init(map, hash_fn, equal_fn, ctx); 608c2ecf20Sopenharmony_ci return map; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_civoid hashmap__clear(struct hashmap *map) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct hashmap_entry *cur, *tmp; 668c2ecf20Sopenharmony_ci size_t bkt; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci hashmap__for_each_entry_safe(map, cur, tmp, bkt) { 698c2ecf20Sopenharmony_ci free(cur); 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci free(map->buckets); 728c2ecf20Sopenharmony_ci map->buckets = NULL; 738c2ecf20Sopenharmony_ci map->cap = map->cap_bits = map->sz = 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_civoid hashmap__free(struct hashmap *map) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci if (!map) 798c2ecf20Sopenharmony_ci return; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci hashmap__clear(map); 828c2ecf20Sopenharmony_ci free(map); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cisize_t hashmap__size(const struct hashmap *map) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci return map->sz; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cisize_t hashmap__capacity(const struct hashmap *map) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci return map->cap; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic bool hashmap_needs_to_grow(struct hashmap *map) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci /* grow if empty or more than 75% filled */ 988c2ecf20Sopenharmony_ci return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int hashmap_grow(struct hashmap *map) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct hashmap_entry **new_buckets; 1048c2ecf20Sopenharmony_ci struct hashmap_entry *cur, *tmp; 1058c2ecf20Sopenharmony_ci size_t new_cap_bits, new_cap; 1068c2ecf20Sopenharmony_ci size_t h, bkt; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci new_cap_bits = map->cap_bits + 1; 1098c2ecf20Sopenharmony_ci if (new_cap_bits < HASHMAP_MIN_CAP_BITS) 1108c2ecf20Sopenharmony_ci new_cap_bits = HASHMAP_MIN_CAP_BITS; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci new_cap = 1UL << new_cap_bits; 1138c2ecf20Sopenharmony_ci new_buckets = calloc(new_cap, sizeof(new_buckets[0])); 1148c2ecf20Sopenharmony_ci if (!new_buckets) 1158c2ecf20Sopenharmony_ci return -ENOMEM; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci hashmap__for_each_entry_safe(map, cur, tmp, bkt) { 1188c2ecf20Sopenharmony_ci h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits); 1198c2ecf20Sopenharmony_ci hashmap_add_entry(&new_buckets[h], cur); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci map->cap = new_cap; 1238c2ecf20Sopenharmony_ci map->cap_bits = new_cap_bits; 1248c2ecf20Sopenharmony_ci free(map->buckets); 1258c2ecf20Sopenharmony_ci map->buckets = new_buckets; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic bool hashmap_find_entry(const struct hashmap *map, 1318c2ecf20Sopenharmony_ci const void *key, size_t hash, 1328c2ecf20Sopenharmony_ci struct hashmap_entry ***pprev, 1338c2ecf20Sopenharmony_ci struct hashmap_entry **entry) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct hashmap_entry *cur, **prev_ptr; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!map->buckets) 1388c2ecf20Sopenharmony_ci return false; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for (prev_ptr = &map->buckets[hash], cur = *prev_ptr; 1418c2ecf20Sopenharmony_ci cur; 1428c2ecf20Sopenharmony_ci prev_ptr = &cur->next, cur = cur->next) { 1438c2ecf20Sopenharmony_ci if (map->equal_fn(cur->key, key, map->ctx)) { 1448c2ecf20Sopenharmony_ci if (pprev) 1458c2ecf20Sopenharmony_ci *pprev = prev_ptr; 1468c2ecf20Sopenharmony_ci *entry = cur; 1478c2ecf20Sopenharmony_ci return true; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return false; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciint hashmap__insert(struct hashmap *map, const void *key, void *value, 1558c2ecf20Sopenharmony_ci enum hashmap_insert_strategy strategy, 1568c2ecf20Sopenharmony_ci const void **old_key, void **old_value) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct hashmap_entry *entry; 1598c2ecf20Sopenharmony_ci size_t h; 1608c2ecf20Sopenharmony_ci int err; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (old_key) 1638c2ecf20Sopenharmony_ci *old_key = NULL; 1648c2ecf20Sopenharmony_ci if (old_value) 1658c2ecf20Sopenharmony_ci *old_value = NULL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 1688c2ecf20Sopenharmony_ci if (strategy != HASHMAP_APPEND && 1698c2ecf20Sopenharmony_ci hashmap_find_entry(map, key, h, NULL, &entry)) { 1708c2ecf20Sopenharmony_ci if (old_key) 1718c2ecf20Sopenharmony_ci *old_key = entry->key; 1728c2ecf20Sopenharmony_ci if (old_value) 1738c2ecf20Sopenharmony_ci *old_value = entry->value; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) { 1768c2ecf20Sopenharmony_ci entry->key = key; 1778c2ecf20Sopenharmony_ci entry->value = value; 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci } else if (strategy == HASHMAP_ADD) { 1808c2ecf20Sopenharmony_ci return -EEXIST; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (strategy == HASHMAP_UPDATE) 1858c2ecf20Sopenharmony_ci return -ENOENT; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (hashmap_needs_to_grow(map)) { 1888c2ecf20Sopenharmony_ci err = hashmap_grow(map); 1898c2ecf20Sopenharmony_ci if (err) 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci entry = malloc(sizeof(struct hashmap_entry)); 1958c2ecf20Sopenharmony_ci if (!entry) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci entry->key = key; 1998c2ecf20Sopenharmony_ci entry->value = value; 2008c2ecf20Sopenharmony_ci hashmap_add_entry(&map->buckets[h], entry); 2018c2ecf20Sopenharmony_ci map->sz++; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cibool hashmap__find(const struct hashmap *map, const void *key, void **value) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct hashmap_entry *entry; 2098c2ecf20Sopenharmony_ci size_t h; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 2128c2ecf20Sopenharmony_ci if (!hashmap_find_entry(map, key, h, NULL, &entry)) 2138c2ecf20Sopenharmony_ci return false; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (value) 2168c2ecf20Sopenharmony_ci *value = entry->value; 2178c2ecf20Sopenharmony_ci return true; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cibool hashmap__delete(struct hashmap *map, const void *key, 2218c2ecf20Sopenharmony_ci const void **old_key, void **old_value) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct hashmap_entry **pprev, *entry; 2248c2ecf20Sopenharmony_ci size_t h; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 2278c2ecf20Sopenharmony_ci if (!hashmap_find_entry(map, key, h, &pprev, &entry)) 2288c2ecf20Sopenharmony_ci return false; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (old_key) 2318c2ecf20Sopenharmony_ci *old_key = entry->key; 2328c2ecf20Sopenharmony_ci if (old_value) 2338c2ecf20Sopenharmony_ci *old_value = entry->value; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci hashmap_del_entry(pprev, entry); 2368c2ecf20Sopenharmony_ci free(entry); 2378c2ecf20Sopenharmony_ci map->sz--; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return true; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 242