255 lines
6.2 KiB
C
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;
|
|
}
|