mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 20:58:22 +00:00
kernel: hashtables
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
#ifndef _HASHTABLE_H
|
||||
#define _HASHTABLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct hashentry_t {
|
||||
struct hashentry_t *next;
|
||||
struct hashentry_t *prev;
|
||||
uintmax_t hash;
|
||||
size_t keysize;
|
||||
void *key;
|
||||
void *value;
|
||||
} hashentry_t;
|
||||
|
||||
typedef struct {
|
||||
size_t capacity;
|
||||
hashentry_t **entries;
|
||||
} hashtable_t;
|
||||
|
||||
int hashtable_init(hashtable_t *table, size_t size);
|
||||
int hashtable_set(hashtable_t *table, void *value, void *key, size_t keysize, bool allocate);
|
||||
int hashtable_get(hashtable_t *table, void **value, void *key, size_t keysize);
|
||||
int hashtable_remove(hashtable_t *table, void *key, size_t keysize);
|
||||
int hashtable_destroy(hashtable_t *table);
|
||||
|
||||
#define HASHTABLE_FOREACH(table) \
|
||||
for (uintmax_t toffset = 0; toffset < table->capacity; ++toffset) \
|
||||
for (hashentry_t *entry = table->entries[toffset]; entry != NULL; entry = entry->next)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
#include <hashtable.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/slab.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FNV1PRIME 0x100000001b3ull
|
||||
#define FNV1OFFSET 0xcbf29ce484222325ull
|
||||
|
||||
static scache_t *hashentrycache;
|
||||
|
||||
// FNV-1a hash implementation
|
||||
static uint64_t hashbuffer(void *buffer, size_t size) {
|
||||
uint8_t *ptr = buffer;
|
||||
uint8_t *top = ptr + size;
|
||||
uint64_t h = FNV1OFFSET;
|
||||
|
||||
while (ptr < top) {
|
||||
h ^= *ptr++;
|
||||
h *= FNV1PRIME;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
static hashentry_t *getentry(hashtable_t *table, void *key, size_t keysize, uintmax_t hash) {
|
||||
uintmax_t tableoffset = hash % table->capacity;
|
||||
|
||||
hashentry_t *entry = table->entries[tableoffset];
|
||||
|
||||
while (entry) {
|
||||
if (entry->keysize == keysize && entry->hash == hash && memcmp(entry->key, key, keysize) == 0)
|
||||
break;
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *table, void *value, void *key, size_t keysize, bool allocate) {
|
||||
uintmax_t hash = hashbuffer(key, keysize);
|
||||
|
||||
hashentry_t *entry = getentry(table, key, keysize, hash);
|
||||
|
||||
if (entry) {
|
||||
entry->value = value;
|
||||
return 0;
|
||||
} else if (allocate) {
|
||||
uintmax_t tableoffset = hash % table->capacity;
|
||||
|
||||
entry = slab_allocate(hashentrycache);
|
||||
if (entry == NULL)
|
||||
return ENOMEM;
|
||||
entry->key = alloc(keysize);
|
||||
if (entry->key == NULL) {
|
||||
slab_free(hashentrycache, entry);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(entry->key, key, keysize);
|
||||
entry->prev = NULL;
|
||||
entry->next = table->entries[tableoffset];
|
||||
entry->value = value;
|
||||
entry->keysize = keysize;
|
||||
entry->hash = hash;
|
||||
table->entries[tableoffset] = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
int hashtable_get(hashtable_t *table, void **value, void *key, size_t keysize) {
|
||||
uintmax_t hash = hashbuffer(key, keysize);
|
||||
|
||||
hashentry_t *entry = getentry(table, key, keysize, hash);
|
||||
|
||||
if (entry == NULL)
|
||||
return ENOENT;
|
||||
|
||||
*value = entry->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashtable_remove(hashtable_t *table, void *key, size_t keysize) {
|
||||
uintmax_t hash = hashbuffer(key, keysize);
|
||||
|
||||
hashentry_t *entry = getentry(table, key, keysize, hash);
|
||||
|
||||
if (entry == NULL)
|
||||
return ENOENT;
|
||||
|
||||
if (entry->prev)
|
||||
entry->prev->next = entry->next;
|
||||
|
||||
if (entry->next)
|
||||
entry->next->prev = entry->prev;
|
||||
|
||||
free(entry->key);
|
||||
slab_free(hashentrycache, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashtable_destroy(hashtable_t *table) {
|
||||
// as to avoid using freed data, keep a copy of the entry
|
||||
hashentry_t entrysave;
|
||||
HASHTABLE_FOREACH(table) {
|
||||
entrysave = *entry;
|
||||
free(entry->key);
|
||||
slab_free(hashentrycache, entry);
|
||||
entry = &entrysave;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashtable_init(hashtable_t *table, size_t size) {
|
||||
// make sure the cache is initialised
|
||||
if (hashentrycache == NULL) {
|
||||
hashentrycache = slab_newcache(sizeof(hashentry_t), 0, NULL, NULL);
|
||||
if (hashentrycache == NULL)
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
table->capacity = size;
|
||||
table->entries = alloc(size * sizeof(hashentry_t *));
|
||||
if (table->entries == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user