basekernel/kernel/hash_set.c

186 lines
4.0 KiB
C

/*
Copyright (C) 2016-2019 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file LICENSE for details.
*/
#include "string.h"
#include "hash_set.h"
#include "kmalloc.h"
#define HASHTABLE_PRIME 31
#define HASHTABLE_GOLDEN_RATIO 0x61C88647
struct hash_set {
unsigned total_buckets;
unsigned num_entries;
struct hash_set_node **head;
};
struct hash_set_node {
unsigned key;
void *data;
struct hash_set_node *next;
};
unsigned hash_string(char *string, unsigned range_min, unsigned range_max)
{
unsigned hash = HASHTABLE_PRIME;
char *curr = string;
while(*curr) {
hash = HASHTABLE_PRIME * hash + *curr;
curr++;
}
return (hash % (range_max - range_min)) + range_min;
}
static unsigned hash_uint(unsigned key, unsigned buckets)
{
return key * HASHTABLE_GOLDEN_RATIO % buckets;
}
static unsigned hash_set_list_add(struct hash_set_node **head, struct hash_set_node *node)
{
struct hash_set_node *prev = 0, *curr = *head;
if(!curr) {
*head = node;
return 0;
}
while(curr && (curr->key < node->key)) {
prev = curr;
curr = curr->next;
}
if(!prev && curr->key != node->key) {
node->next = *head;
*head = node;
return 0;
} else if(!curr) {
prev->next = node;
return 0;
} else if(curr->key != node->key) {
node->next = curr->next;
curr->next = node;
return 0;
}
return -1;
}
static struct hash_set_node *hash_set_list_lookup(struct hash_set_node *head, unsigned key)
{
struct hash_set_node *curr = head;
while(curr && (curr->key < key)) {
curr = curr->next;
}
return (curr && (curr->key == key)) ? curr : 0;
}
static unsigned hash_set_list_delete(struct hash_set_node **head, unsigned key)
{
struct hash_set_node *prev = 0, *curr = *head;
while(curr && (curr->key < key)) {
prev = curr;
curr = curr->next;
}
if(curr && (curr->key == key)) {
if(prev)
prev->next = curr->next;
if(curr == *head)
*head = curr->next;
kfree(curr);
return 0;
}
return -1;
}
struct hash_set *hash_set_create(unsigned buckets)
{
struct hash_set_node **set_nodes = kmalloc(sizeof(struct hash_set_node *) * buckets);
struct hash_set *set = kmalloc(sizeof(struct hash_set));
memset(set_nodes, 0, sizeof(struct hash_set_node *) * buckets);
set->total_buckets = buckets;
set->head = set_nodes;
set->num_entries = 0;
if(!set || !set_nodes) {
hash_set_delete(set);
return 0;
}
return set;
}
static unsigned hash_set_list_dealloc(struct hash_set_node *head)
{
struct hash_set_node *next = head, *curr = head;
while(curr) {
next = curr->next;
kfree(curr);
curr = next;
}
return 0;
}
void hash_set_delete(struct hash_set *set)
{
struct hash_set_node **set_nodes = set->head;
if(set)
kfree(set);
if(set_nodes) {
unsigned i;
for(i = 0; i < set->total_buckets; i++)
hash_set_list_dealloc(set->head[i]);
kfree(set_nodes);
}
}
unsigned hash_set_add(struct hash_set *set, unsigned key, void *data)
{
unsigned hash_key = hash_uint(key, set->total_buckets);
struct hash_set_node *node = kmalloc(sizeof(struct hash_set_node));
node->key = key;
node->data = data;
node->next = 0;
unsigned ret = hash_set_list_add(&(set->head[hash_key]), node);
if(ret == 0)
set->num_entries++;
return ret;
}
void * hash_set_lookup(struct hash_set * set, unsigned key)
{
unsigned hash_key = hash_uint(key, set->total_buckets);
struct hash_set_node *result = hash_set_list_lookup(set->head[hash_key], key);
if(result) {
return result->data;
} else {
return 0;
}
}
unsigned hash_set_remove(struct hash_set *set, unsigned key)
{
unsigned hash_key = hash_uint(key, set->total_buckets);
unsigned result = hash_set_list_delete(&(set->head[hash_key]), key);
if(result == 0)
set->num_entries--;
return result;
}
unsigned hash_set_entries( struct hash_set *set )
{
return set->num_entries;
}
void hash_set_print(struct hash_set *set)
{
unsigned i;
printf("printing hash set:\n");
for(i = 0; i < set->total_buckets; i++) {
struct hash_set_node *start = set->head[i];
while(start) {
printf("%u: %u\n", i, start->key);
start = start->next;
}
}
}