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