Files
MentOS/mentos/src/klib/hashmap.c
T
2021-10-05 14:29:24 +02:00

255 lines
6.2 KiB
C

/// MentOS, The Mentoring Operating system project
/// @file hashmap.c
/// @brief
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#include "klib/hashmap.h"
#include "assert.h"
#include "string.h"
#include "mem/slab.h"
/// @brief Stores information of an entry of the hashmap.
struct hashmap_entry_t {
/// Key of the entry.
char *key;
/// Value of the entry.
void *value;
/// Pointer to the next entry.
struct hashmap_entry_t *next;
};
/// @brief Stores information of a hashmap.
struct hashmap_t {
/// Hashing function, used to generate hash keys.
hashmap_hash_t hash_func;
/// Comparison function, used to compare hash keys.
hashmap_comp_t hash_comp;
/// Key duplication function, used to duplicate hash keys.
hashmap_dupe_t hash_key_dup;
/// Key deallocation function, used to free the memory occupied by hash keys.
hashmap_free_t hash_key_free;
/// Size of the hashmap.
unsigned int size;
/// List of entries.
hashmap_entry_t **entries;
};
static inline hashmap_t *__alloc_hashmap()
{
hashmap_t *hashmap = kmalloc(sizeof(hashmap_t));
memset(hashmap, 0, sizeof(hashmap_t));
return hashmap;
}
static inline hashmap_entry_t *__alloc_entry()
{
hashmap_entry_t *entry = kmalloc(sizeof(hashmap_entry_t));
memset(entry, 0, sizeof(hashmap_entry_t));
return entry;
}
static inline void __dealloc_entry(hashmap_entry_t *entry)
{
assert(entry && "Invalid pointer to an entry.");
kfree(entry);
}
static inline hashmap_entry_t **__alloc_entries(unsigned int size)
{
hashmap_entry_t **entries = kmalloc(sizeof(hashmap_entry_t *) * size);
memset(entries, 0, sizeof(hashmap_entry_t *) * size);
return entries;
}
static inline void __dealloc_entries(hashmap_entry_t **entries)
{
assert(entries && "Invalid pointer to entries.");
kfree(entries);
}
unsigned int hashmap_int_hash(const void *key)
{
return (unsigned int)key;
}
int hashmap_int_comp(const void *a, const void *b)
{
return (int)a == (int)b;
}
unsigned int hashmap_str_hash(const void *_key)
{
unsigned int hash = 0;
const char *key = (const char *)_key;
char c;
// This is the so-called "sdbm" hash. It comes from a piece of public
// domain code from a clone of ndbm.
while ((c = *key++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}
int hashmap_str_comp(const void *a, const void *b)
{
return !strcmp(a, b);
}
void *hashmap_do_not_duplicate(const void *value)
{
return (void *)value;
}
void hashmap_do_not_free(void *value)
{
(void)value;
}
hashmap_t *hashmap_create(
unsigned int size,
hashmap_hash_t hash_fun,
hashmap_comp_t comp_fun,
hashmap_dupe_t dupe_fun,
hashmap_free_t key_free_fun)
{
// Allocate the map.
hashmap_t *map = __alloc_hashmap();
// Initialize the entries.
map->size = size;
map->entries = __alloc_entries(size);
// Initialize its functions.
map->hash_func = hash_fun;
map->hash_comp = comp_fun;
map->hash_key_dup = dupe_fun;
map->hash_key_free = key_free_fun;
return map;
}
void hashmap_free(hashmap_t *map)
{
for (unsigned int i = 0; i < map->size; ++i) {
hashmap_entry_t *x = map->entries[i], *p;
while (x) {
p = x;
x = x->next;
map->hash_key_free(p->key);
__dealloc_entry(p);
}
}
__dealloc_entries(map->entries);
}
void *hashmap_set(hashmap_t *map, const void *key, void *value)
{
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t *x = map->entries[hash];
if (x == NULL) {
hashmap_entry_t *e = __alloc_entry();
e->key = map->hash_key_dup(key);
e->value = value;
e->next = NULL;
map->entries[hash] = e;
return NULL;
}
hashmap_entry_t *p = NULL;
do {
if (map->hash_comp(x->key, key)) {
void *out = x->value;
x->value = value;
return out;
}
p = x;
x = x->next;
} while (x);
hashmap_entry_t *e = __alloc_entry();
e->key = map->hash_key_dup(key);
e->value = value;
e->next = NULL;
p->next = e;
return NULL;
}
void *hashmap_get(hashmap_t *map, const void *key)
{
unsigned int hash = map->hash_func(key) % map->size;
for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next)
if (map->hash_comp(x->key, key))
return x->value;
return NULL;
}
void *hashmap_remove(hashmap_t *map, const void *key)
{
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t *x = map->entries[hash];
if (x == NULL) {
return NULL;
}
if (map->hash_comp(x->key, key)) {
void *out = x->value;
map->entries[hash] = x->next;
map->hash_key_free(x->key);
__dealloc_entry(x);
return out;
}
hashmap_entry_t *p = x;
x = x->next;
do {
if (map->hash_comp(x->key, key)) {
void *out = x->value;
p->next = x->next;
map->hash_key_free(x->key);
__dealloc_entry(x);
return out;
}
p = x;
x = x->next;
} while (x);
return NULL;
}
int hashmap_is_empty(hashmap_t *map)
{
for (unsigned int i = 0; i < map->size; ++i)
if (map->entries[i])
return 0;
return 1;
}
int hashmap_has(hashmap_t *map, const void *key)
{
unsigned int hash = map->hash_func(key) % map->size;
for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next)
if (map->hash_comp(x->key, key))
return 1;
return 0;
}
list_t *hashmap_keys(hashmap_t *map)
{
list_t *l = list_create();
for (unsigned int i = 0; i < map->size; ++i)
for (hashmap_entry_t *x = map->entries[i]; x; x = x->next)
list_insert_back(l, x->key);
return l;
}
list_t *hashmap_values(hashmap_t *map)
{
list_t *l = list_create();
for (unsigned int i = 0; i < map->size; ++i)
for (hashmap_entry_t *x = map->entries[i]; x; x = x->next)
list_insert_back(l, x->value);
return l;
}