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