162306a36Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Generic non-thread safe hash map implementation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2019 Facebook 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <stdint.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <errno.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include "hashmap.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* make sure libbpf doesn't use kernel-only integer typedefs */ 1662306a36Sopenharmony_ci#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* prevent accidental re-addition of reallocarray() */ 1962306a36Sopenharmony_ci#pragma GCC poison reallocarray 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* start with 4 buckets */ 2262306a36Sopenharmony_ci#define HASHMAP_MIN_CAP_BITS 2 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic void hashmap_add_entry(struct hashmap_entry **pprev, 2562306a36Sopenharmony_ci struct hashmap_entry *entry) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci entry->next = *pprev; 2862306a36Sopenharmony_ci *pprev = entry; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void hashmap_del_entry(struct hashmap_entry **pprev, 3262306a36Sopenharmony_ci struct hashmap_entry *entry) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci *pprev = entry->next; 3562306a36Sopenharmony_ci entry->next = NULL; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_civoid hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn, 3962306a36Sopenharmony_ci hashmap_equal_fn equal_fn, void *ctx) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci map->hash_fn = hash_fn; 4262306a36Sopenharmony_ci map->equal_fn = equal_fn; 4362306a36Sopenharmony_ci map->ctx = ctx; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci map->buckets = NULL; 4662306a36Sopenharmony_ci map->cap = 0; 4762306a36Sopenharmony_ci map->cap_bits = 0; 4862306a36Sopenharmony_ci map->sz = 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct hashmap *hashmap__new(hashmap_hash_fn hash_fn, 5262306a36Sopenharmony_ci hashmap_equal_fn equal_fn, 5362306a36Sopenharmony_ci void *ctx) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct hashmap *map = malloc(sizeof(struct hashmap)); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (!map) 5862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 5962306a36Sopenharmony_ci hashmap__init(map, hash_fn, equal_fn, ctx); 6062306a36Sopenharmony_ci return map; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_civoid hashmap__clear(struct hashmap *map) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct hashmap_entry *cur, *tmp; 6662306a36Sopenharmony_ci size_t bkt; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci hashmap__for_each_entry_safe(map, cur, tmp, bkt) { 6962306a36Sopenharmony_ci free(cur); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci free(map->buckets); 7262306a36Sopenharmony_ci map->buckets = NULL; 7362306a36Sopenharmony_ci map->cap = map->cap_bits = map->sz = 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_civoid hashmap__free(struct hashmap *map) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci if (IS_ERR_OR_NULL(map)) 7962306a36Sopenharmony_ci return; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci hashmap__clear(map); 8262306a36Sopenharmony_ci free(map); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cisize_t hashmap__size(const struct hashmap *map) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return map->sz; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cisize_t hashmap__capacity(const struct hashmap *map) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci return map->cap; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic bool hashmap_needs_to_grow(struct hashmap *map) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci /* grow if empty or more than 75% filled */ 9862306a36Sopenharmony_ci return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int hashmap_grow(struct hashmap *map) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct hashmap_entry **new_buckets; 10462306a36Sopenharmony_ci struct hashmap_entry *cur, *tmp; 10562306a36Sopenharmony_ci size_t new_cap_bits, new_cap; 10662306a36Sopenharmony_ci size_t h, bkt; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci new_cap_bits = map->cap_bits + 1; 10962306a36Sopenharmony_ci if (new_cap_bits < HASHMAP_MIN_CAP_BITS) 11062306a36Sopenharmony_ci new_cap_bits = HASHMAP_MIN_CAP_BITS; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci new_cap = 1UL << new_cap_bits; 11362306a36Sopenharmony_ci new_buckets = calloc(new_cap, sizeof(new_buckets[0])); 11462306a36Sopenharmony_ci if (!new_buckets) 11562306a36Sopenharmony_ci return -ENOMEM; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci hashmap__for_each_entry_safe(map, cur, tmp, bkt) { 11862306a36Sopenharmony_ci h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits); 11962306a36Sopenharmony_ci hashmap_add_entry(&new_buckets[h], cur); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci map->cap = new_cap; 12362306a36Sopenharmony_ci map->cap_bits = new_cap_bits; 12462306a36Sopenharmony_ci free(map->buckets); 12562306a36Sopenharmony_ci map->buckets = new_buckets; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic bool hashmap_find_entry(const struct hashmap *map, 13162306a36Sopenharmony_ci const long key, size_t hash, 13262306a36Sopenharmony_ci struct hashmap_entry ***pprev, 13362306a36Sopenharmony_ci struct hashmap_entry **entry) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct hashmap_entry *cur, **prev_ptr; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!map->buckets) 13862306a36Sopenharmony_ci return false; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci for (prev_ptr = &map->buckets[hash], cur = *prev_ptr; 14162306a36Sopenharmony_ci cur; 14262306a36Sopenharmony_ci prev_ptr = &cur->next, cur = cur->next) { 14362306a36Sopenharmony_ci if (map->equal_fn(cur->key, key, map->ctx)) { 14462306a36Sopenharmony_ci if (pprev) 14562306a36Sopenharmony_ci *pprev = prev_ptr; 14662306a36Sopenharmony_ci *entry = cur; 14762306a36Sopenharmony_ci return true; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return false; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciint hashmap_insert(struct hashmap *map, long key, long value, 15562306a36Sopenharmony_ci enum hashmap_insert_strategy strategy, 15662306a36Sopenharmony_ci long *old_key, long *old_value) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct hashmap_entry *entry; 15962306a36Sopenharmony_ci size_t h; 16062306a36Sopenharmony_ci int err; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (old_key) 16362306a36Sopenharmony_ci *old_key = 0; 16462306a36Sopenharmony_ci if (old_value) 16562306a36Sopenharmony_ci *old_value = 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 16862306a36Sopenharmony_ci if (strategy != HASHMAP_APPEND && 16962306a36Sopenharmony_ci hashmap_find_entry(map, key, h, NULL, &entry)) { 17062306a36Sopenharmony_ci if (old_key) 17162306a36Sopenharmony_ci *old_key = entry->key; 17262306a36Sopenharmony_ci if (old_value) 17362306a36Sopenharmony_ci *old_value = entry->value; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) { 17662306a36Sopenharmony_ci entry->key = key; 17762306a36Sopenharmony_ci entry->value = value; 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci } else if (strategy == HASHMAP_ADD) { 18062306a36Sopenharmony_ci return -EEXIST; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (strategy == HASHMAP_UPDATE) 18562306a36Sopenharmony_ci return -ENOENT; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (hashmap_needs_to_grow(map)) { 18862306a36Sopenharmony_ci err = hashmap_grow(map); 18962306a36Sopenharmony_ci if (err) 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci entry = malloc(sizeof(struct hashmap_entry)); 19562306a36Sopenharmony_ci if (!entry) 19662306a36Sopenharmony_ci return -ENOMEM; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci entry->key = key; 19962306a36Sopenharmony_ci entry->value = value; 20062306a36Sopenharmony_ci hashmap_add_entry(&map->buckets[h], entry); 20162306a36Sopenharmony_ci map->sz++; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cibool hashmap_find(const struct hashmap *map, long key, long *value) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct hashmap_entry *entry; 20962306a36Sopenharmony_ci size_t h; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 21262306a36Sopenharmony_ci if (!hashmap_find_entry(map, key, h, NULL, &entry)) 21362306a36Sopenharmony_ci return false; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (value) 21662306a36Sopenharmony_ci *value = entry->value; 21762306a36Sopenharmony_ci return true; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cibool hashmap_delete(struct hashmap *map, long key, 22162306a36Sopenharmony_ci long *old_key, long *old_value) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct hashmap_entry **pprev, *entry; 22462306a36Sopenharmony_ci size_t h; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits); 22762306a36Sopenharmony_ci if (!hashmap_find_entry(map, key, h, &pprev, &entry)) 22862306a36Sopenharmony_ci return false; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (old_key) 23162306a36Sopenharmony_ci *old_key = entry->key; 23262306a36Sopenharmony_ci if (old_value) 23362306a36Sopenharmony_ci *old_value = entry->value; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci hashmap_del_entry(pprev, entry); 23662306a36Sopenharmony_ci free(entry); 23762306a36Sopenharmony_ci map->sz--; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return true; 24062306a36Sopenharmony_ci} 241