186 lines
4.0 KiB
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;
|
|
}
|
|
}
|
|
}
|