/* 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; } } }