17c2aad20Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
27c2aad20Sopenharmony_ci
37c2aad20Sopenharmony_ci/*
47c2aad20Sopenharmony_ci * Generic non-thread safe hash map implementation.
57c2aad20Sopenharmony_ci *
67c2aad20Sopenharmony_ci * Copyright (c) 2019 Facebook
77c2aad20Sopenharmony_ci */
87c2aad20Sopenharmony_ci#include <stdint.h>
97c2aad20Sopenharmony_ci#include <stdlib.h>
107c2aad20Sopenharmony_ci#include <stdio.h>
117c2aad20Sopenharmony_ci#include <errno.h>
127c2aad20Sopenharmony_ci#include <linux/err.h>
137c2aad20Sopenharmony_ci#include "hashmap.h"
147c2aad20Sopenharmony_ci
157c2aad20Sopenharmony_ci/* make sure libbpf doesn't use kernel-only integer typedefs */
167c2aad20Sopenharmony_ci#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
177c2aad20Sopenharmony_ci
187c2aad20Sopenharmony_ci/* prevent accidental re-addition of reallocarray() */
197c2aad20Sopenharmony_ci#pragma GCC poison reallocarray
207c2aad20Sopenharmony_ci
217c2aad20Sopenharmony_ci/* start with 4 buckets */
227c2aad20Sopenharmony_ci#define HASHMAP_MIN_CAP_BITS 2
237c2aad20Sopenharmony_ci
247c2aad20Sopenharmony_cistatic void hashmap_add_entry(struct hashmap_entry **pprev,
257c2aad20Sopenharmony_ci			      struct hashmap_entry *entry)
267c2aad20Sopenharmony_ci{
277c2aad20Sopenharmony_ci	entry->next = *pprev;
287c2aad20Sopenharmony_ci	*pprev = entry;
297c2aad20Sopenharmony_ci}
307c2aad20Sopenharmony_ci
317c2aad20Sopenharmony_cistatic void hashmap_del_entry(struct hashmap_entry **pprev,
327c2aad20Sopenharmony_ci			      struct hashmap_entry *entry)
337c2aad20Sopenharmony_ci{
347c2aad20Sopenharmony_ci	*pprev = entry->next;
357c2aad20Sopenharmony_ci	entry->next = NULL;
367c2aad20Sopenharmony_ci}
377c2aad20Sopenharmony_ci
387c2aad20Sopenharmony_civoid hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
397c2aad20Sopenharmony_ci		   hashmap_equal_fn equal_fn, void *ctx)
407c2aad20Sopenharmony_ci{
417c2aad20Sopenharmony_ci	map->hash_fn = hash_fn;
427c2aad20Sopenharmony_ci	map->equal_fn = equal_fn;
437c2aad20Sopenharmony_ci	map->ctx = ctx;
447c2aad20Sopenharmony_ci
457c2aad20Sopenharmony_ci	map->buckets = NULL;
467c2aad20Sopenharmony_ci	map->cap = 0;
477c2aad20Sopenharmony_ci	map->cap_bits = 0;
487c2aad20Sopenharmony_ci	map->sz = 0;
497c2aad20Sopenharmony_ci}
507c2aad20Sopenharmony_ci
517c2aad20Sopenharmony_cistruct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
527c2aad20Sopenharmony_ci			     hashmap_equal_fn equal_fn,
537c2aad20Sopenharmony_ci			     void *ctx)
547c2aad20Sopenharmony_ci{
557c2aad20Sopenharmony_ci	struct hashmap *map = malloc(sizeof(struct hashmap));
567c2aad20Sopenharmony_ci
577c2aad20Sopenharmony_ci	if (!map)
587c2aad20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
597c2aad20Sopenharmony_ci	hashmap__init(map, hash_fn, equal_fn, ctx);
607c2aad20Sopenharmony_ci	return map;
617c2aad20Sopenharmony_ci}
627c2aad20Sopenharmony_ci
637c2aad20Sopenharmony_civoid hashmap__clear(struct hashmap *map)
647c2aad20Sopenharmony_ci{
657c2aad20Sopenharmony_ci	struct hashmap_entry *cur, *tmp;
667c2aad20Sopenharmony_ci	size_t bkt;
677c2aad20Sopenharmony_ci
687c2aad20Sopenharmony_ci	hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
697c2aad20Sopenharmony_ci		free(cur);
707c2aad20Sopenharmony_ci	}
717c2aad20Sopenharmony_ci	free(map->buckets);
727c2aad20Sopenharmony_ci	map->buckets = NULL;
737c2aad20Sopenharmony_ci	map->cap = map->cap_bits = map->sz = 0;
747c2aad20Sopenharmony_ci}
757c2aad20Sopenharmony_ci
767c2aad20Sopenharmony_civoid hashmap__free(struct hashmap *map)
777c2aad20Sopenharmony_ci{
787c2aad20Sopenharmony_ci	if (IS_ERR_OR_NULL(map))
797c2aad20Sopenharmony_ci		return;
807c2aad20Sopenharmony_ci
817c2aad20Sopenharmony_ci	hashmap__clear(map);
827c2aad20Sopenharmony_ci	free(map);
837c2aad20Sopenharmony_ci}
847c2aad20Sopenharmony_ci
857c2aad20Sopenharmony_cisize_t hashmap__size(const struct hashmap *map)
867c2aad20Sopenharmony_ci{
877c2aad20Sopenharmony_ci	return map->sz;
887c2aad20Sopenharmony_ci}
897c2aad20Sopenharmony_ci
907c2aad20Sopenharmony_cisize_t hashmap__capacity(const struct hashmap *map)
917c2aad20Sopenharmony_ci{
927c2aad20Sopenharmony_ci	return map->cap;
937c2aad20Sopenharmony_ci}
947c2aad20Sopenharmony_ci
957c2aad20Sopenharmony_cistatic bool hashmap_needs_to_grow(struct hashmap *map)
967c2aad20Sopenharmony_ci{
977c2aad20Sopenharmony_ci	/* grow if empty or more than 75% filled */
987c2aad20Sopenharmony_ci	return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
997c2aad20Sopenharmony_ci}
1007c2aad20Sopenharmony_ci
1017c2aad20Sopenharmony_cistatic int hashmap_grow(struct hashmap *map)
1027c2aad20Sopenharmony_ci{
1037c2aad20Sopenharmony_ci	struct hashmap_entry **new_buckets;
1047c2aad20Sopenharmony_ci	struct hashmap_entry *cur, *tmp;
1057c2aad20Sopenharmony_ci	size_t new_cap_bits, new_cap;
1067c2aad20Sopenharmony_ci	size_t h, bkt;
1077c2aad20Sopenharmony_ci
1087c2aad20Sopenharmony_ci	new_cap_bits = map->cap_bits + 1;
1097c2aad20Sopenharmony_ci	if (new_cap_bits < HASHMAP_MIN_CAP_BITS)
1107c2aad20Sopenharmony_ci		new_cap_bits = HASHMAP_MIN_CAP_BITS;
1117c2aad20Sopenharmony_ci
1127c2aad20Sopenharmony_ci	new_cap = 1UL << new_cap_bits;
1137c2aad20Sopenharmony_ci	new_buckets = calloc(new_cap, sizeof(new_buckets[0]));
1147c2aad20Sopenharmony_ci	if (!new_buckets)
1157c2aad20Sopenharmony_ci		return -ENOMEM;
1167c2aad20Sopenharmony_ci
1177c2aad20Sopenharmony_ci	hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
1187c2aad20Sopenharmony_ci		h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
1197c2aad20Sopenharmony_ci		hashmap_add_entry(&new_buckets[h], cur);
1207c2aad20Sopenharmony_ci	}
1217c2aad20Sopenharmony_ci
1227c2aad20Sopenharmony_ci	map->cap = new_cap;
1237c2aad20Sopenharmony_ci	map->cap_bits = new_cap_bits;
1247c2aad20Sopenharmony_ci	free(map->buckets);
1257c2aad20Sopenharmony_ci	map->buckets = new_buckets;
1267c2aad20Sopenharmony_ci
1277c2aad20Sopenharmony_ci	return 0;
1287c2aad20Sopenharmony_ci}
1297c2aad20Sopenharmony_ci
1307c2aad20Sopenharmony_cistatic bool hashmap_find_entry(const struct hashmap *map,
1317c2aad20Sopenharmony_ci			       const long key, size_t hash,
1327c2aad20Sopenharmony_ci			       struct hashmap_entry ***pprev,
1337c2aad20Sopenharmony_ci			       struct hashmap_entry **entry)
1347c2aad20Sopenharmony_ci{
1357c2aad20Sopenharmony_ci	struct hashmap_entry *cur, **prev_ptr;
1367c2aad20Sopenharmony_ci
1377c2aad20Sopenharmony_ci	if (!map->buckets)
1387c2aad20Sopenharmony_ci		return false;
1397c2aad20Sopenharmony_ci
1407c2aad20Sopenharmony_ci	for (prev_ptr = &map->buckets[hash], cur = *prev_ptr;
1417c2aad20Sopenharmony_ci	     cur;
1427c2aad20Sopenharmony_ci	     prev_ptr = &cur->next, cur = cur->next) {
1437c2aad20Sopenharmony_ci		if (map->equal_fn(cur->key, key, map->ctx)) {
1447c2aad20Sopenharmony_ci			if (pprev)
1457c2aad20Sopenharmony_ci				*pprev = prev_ptr;
1467c2aad20Sopenharmony_ci			*entry = cur;
1477c2aad20Sopenharmony_ci			return true;
1487c2aad20Sopenharmony_ci		}
1497c2aad20Sopenharmony_ci	}
1507c2aad20Sopenharmony_ci
1517c2aad20Sopenharmony_ci	return false;
1527c2aad20Sopenharmony_ci}
1537c2aad20Sopenharmony_ci
1547c2aad20Sopenharmony_ciint hashmap_insert(struct hashmap *map, long key, long value,
1557c2aad20Sopenharmony_ci		   enum hashmap_insert_strategy strategy,
1567c2aad20Sopenharmony_ci		   long *old_key, long *old_value)
1577c2aad20Sopenharmony_ci{
1587c2aad20Sopenharmony_ci	struct hashmap_entry *entry;
1597c2aad20Sopenharmony_ci	size_t h;
1607c2aad20Sopenharmony_ci	int err;
1617c2aad20Sopenharmony_ci
1627c2aad20Sopenharmony_ci	if (old_key)
1637c2aad20Sopenharmony_ci		*old_key = 0;
1647c2aad20Sopenharmony_ci	if (old_value)
1657c2aad20Sopenharmony_ci		*old_value = 0;
1667c2aad20Sopenharmony_ci
1677c2aad20Sopenharmony_ci	h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
1687c2aad20Sopenharmony_ci	if (strategy != HASHMAP_APPEND &&
1697c2aad20Sopenharmony_ci	    hashmap_find_entry(map, key, h, NULL, &entry)) {
1707c2aad20Sopenharmony_ci		if (old_key)
1717c2aad20Sopenharmony_ci			*old_key = entry->key;
1727c2aad20Sopenharmony_ci		if (old_value)
1737c2aad20Sopenharmony_ci			*old_value = entry->value;
1747c2aad20Sopenharmony_ci
1757c2aad20Sopenharmony_ci		if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) {
1767c2aad20Sopenharmony_ci			entry->key = key;
1777c2aad20Sopenharmony_ci			entry->value = value;
1787c2aad20Sopenharmony_ci			return 0;
1797c2aad20Sopenharmony_ci		} else if (strategy == HASHMAP_ADD) {
1807c2aad20Sopenharmony_ci			return -EEXIST;
1817c2aad20Sopenharmony_ci		}
1827c2aad20Sopenharmony_ci	}
1837c2aad20Sopenharmony_ci
1847c2aad20Sopenharmony_ci	if (strategy == HASHMAP_UPDATE)
1857c2aad20Sopenharmony_ci		return -ENOENT;
1867c2aad20Sopenharmony_ci
1877c2aad20Sopenharmony_ci	if (hashmap_needs_to_grow(map)) {
1887c2aad20Sopenharmony_ci		err = hashmap_grow(map);
1897c2aad20Sopenharmony_ci		if (err)
1907c2aad20Sopenharmony_ci			return err;
1917c2aad20Sopenharmony_ci		h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
1927c2aad20Sopenharmony_ci	}
1937c2aad20Sopenharmony_ci
1947c2aad20Sopenharmony_ci	entry = malloc(sizeof(struct hashmap_entry));
1957c2aad20Sopenharmony_ci	if (!entry)
1967c2aad20Sopenharmony_ci		return -ENOMEM;
1977c2aad20Sopenharmony_ci
1987c2aad20Sopenharmony_ci	entry->key = key;
1997c2aad20Sopenharmony_ci	entry->value = value;
2007c2aad20Sopenharmony_ci	hashmap_add_entry(&map->buckets[h], entry);
2017c2aad20Sopenharmony_ci	map->sz++;
2027c2aad20Sopenharmony_ci
2037c2aad20Sopenharmony_ci	return 0;
2047c2aad20Sopenharmony_ci}
2057c2aad20Sopenharmony_ci
2067c2aad20Sopenharmony_cibool hashmap_find(const struct hashmap *map, long key, long *value)
2077c2aad20Sopenharmony_ci{
2087c2aad20Sopenharmony_ci	struct hashmap_entry *entry;
2097c2aad20Sopenharmony_ci	size_t h;
2107c2aad20Sopenharmony_ci
2117c2aad20Sopenharmony_ci	h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
2127c2aad20Sopenharmony_ci	if (!hashmap_find_entry(map, key, h, NULL, &entry))
2137c2aad20Sopenharmony_ci		return false;
2147c2aad20Sopenharmony_ci
2157c2aad20Sopenharmony_ci	if (value)
2167c2aad20Sopenharmony_ci		*value = entry->value;
2177c2aad20Sopenharmony_ci	return true;
2187c2aad20Sopenharmony_ci}
2197c2aad20Sopenharmony_ci
2207c2aad20Sopenharmony_cibool hashmap_delete(struct hashmap *map, long key,
2217c2aad20Sopenharmony_ci		    long *old_key, long *old_value)
2227c2aad20Sopenharmony_ci{
2237c2aad20Sopenharmony_ci	struct hashmap_entry **pprev, *entry;
2247c2aad20Sopenharmony_ci	size_t h;
2257c2aad20Sopenharmony_ci
2267c2aad20Sopenharmony_ci	h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
2277c2aad20Sopenharmony_ci	if (!hashmap_find_entry(map, key, h, &pprev, &entry))
2287c2aad20Sopenharmony_ci		return false;
2297c2aad20Sopenharmony_ci
2307c2aad20Sopenharmony_ci	if (old_key)
2317c2aad20Sopenharmony_ci		*old_key = entry->key;
2327c2aad20Sopenharmony_ci	if (old_value)
2337c2aad20Sopenharmony_ci		*old_value = entry->value;
2347c2aad20Sopenharmony_ci
2357c2aad20Sopenharmony_ci	hashmap_del_entry(pprev, entry);
2367c2aad20Sopenharmony_ci	free(entry);
2377c2aad20Sopenharmony_ci	map->sz--;
2387c2aad20Sopenharmony_ci
2397c2aad20Sopenharmony_ci	return true;
2407c2aad20Sopenharmony_ci}
241