^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Generic non-thread safe hash map implementation.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (c) 2019 Facebook
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <stdint.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <stdlib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <stdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include "hashmap.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) /* make sure libbpf doesn't use kernel-only integer typedefs */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) /* prevent accidental re-addition of reallocarray() */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #pragma GCC poison reallocarray
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) /* start with 4 buckets */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define HASHMAP_MIN_CAP_BITS 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static void hashmap_add_entry(struct hashmap_entry **pprev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) struct hashmap_entry *entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) entry->next = *pprev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) *pprev = entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) static void hashmap_del_entry(struct hashmap_entry **pprev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct hashmap_entry *entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) *pprev = entry->next;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) entry->next = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) hashmap_equal_fn equal_fn, void *ctx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) map->hash_fn = hash_fn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) map->equal_fn = equal_fn;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) map->ctx = ctx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) map->buckets = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) map->cap = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) map->cap_bits = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) map->sz = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) hashmap_equal_fn equal_fn,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) void *ctx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) struct hashmap *map = malloc(sizeof(struct hashmap));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) if (!map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return ERR_PTR(-ENOMEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) hashmap__init(map, hash_fn, equal_fn, ctx);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) return map;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) void hashmap__clear(struct hashmap *map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) struct hashmap_entry *cur, *tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) size_t bkt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) free(cur);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) free(map->buckets);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) map->buckets = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) map->cap = map->cap_bits = map->sz = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) void hashmap__free(struct hashmap *map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) if (!map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) hashmap__clear(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) free(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) size_t hashmap__size(const struct hashmap *map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) return map->sz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) size_t hashmap__capacity(const struct hashmap *map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return map->cap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static bool hashmap_needs_to_grow(struct hashmap *map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) /* grow if empty or more than 75% filled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static int hashmap_grow(struct hashmap *map)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct hashmap_entry **new_buckets;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct hashmap_entry *cur, *tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) size_t new_cap_bits, new_cap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) size_t h, bkt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) new_cap_bits = map->cap_bits + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (new_cap_bits < HASHMAP_MIN_CAP_BITS)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) new_cap_bits = HASHMAP_MIN_CAP_BITS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) new_cap = 1UL << new_cap_bits;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) new_buckets = calloc(new_cap, sizeof(new_buckets[0]));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (!new_buckets)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) hashmap_add_entry(&new_buckets[h], cur);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) map->cap = new_cap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) map->cap_bits = new_cap_bits;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) free(map->buckets);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) map->buckets = new_buckets;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) static bool hashmap_find_entry(const struct hashmap *map,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) const void *key, size_t hash,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) struct hashmap_entry ***pprev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) struct hashmap_entry **entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) struct hashmap_entry *cur, **prev_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (!map->buckets)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) for (prev_ptr = &map->buckets[hash], cur = *prev_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) cur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) prev_ptr = &cur->next, cur = cur->next) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) if (map->equal_fn(cur->key, key, map->ctx)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (pprev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) *pprev = prev_ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) *entry = cur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) int hashmap__insert(struct hashmap *map, const void *key, void *value,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) enum hashmap_insert_strategy strategy,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) const void **old_key, void **old_value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) struct hashmap_entry *entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) size_t h;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) if (old_key)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) *old_key = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) if (old_value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) *old_value = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) if (strategy != HASHMAP_APPEND &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) hashmap_find_entry(map, key, h, NULL, &entry)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if (old_key)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) *old_key = entry->key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) if (old_value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) *old_value = entry->value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) entry->key = key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) entry->value = value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) } else if (strategy == HASHMAP_ADD) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) return -EEXIST;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (strategy == HASHMAP_UPDATE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) if (hashmap_needs_to_grow(map)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) err = hashmap_grow(map);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) entry = malloc(sizeof(struct hashmap_entry));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) if (!entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) entry->key = key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) entry->value = value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) hashmap_add_entry(&map->buckets[h], entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) map->sz++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) bool hashmap__find(const struct hashmap *map, const void *key, void **value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) struct hashmap_entry *entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) size_t h;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (!hashmap_find_entry(map, key, h, NULL, &entry))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) if (value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) *value = entry->value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) bool hashmap__delete(struct hashmap *map, const void *key,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) const void **old_key, void **old_value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) struct hashmap_entry **pprev, *entry;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) size_t h;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) if (!hashmap_find_entry(map, key, h, &pprev, &entry))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) if (old_key)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) *old_key = entry->key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) if (old_value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) *old_value = entry->value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) hashmap_del_entry(pprev, entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) free(entry);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) map->sz--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241)