diff --git a/kernel/hash_set.c b/kernel/hash_set.c new file mode 100644 index 0000000..325f085 --- /dev/null +++ b/kernel/hash_set.c @@ -0,0 +1,185 @@ +/* +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; + } + } +}