aos/lib/collections/hash_table.c
Daniel Schwyn 6d444bf552 Main handout
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-03-03 14:57:51 +01:00

314 lines
8.0 KiB
C

/**
* \file
* \brief Barrelfish collections library hash table
*/
/*
* Copyright (c) 2010, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
*/
#include "collections/hash_table.h"
#include "inttypes.h"
/******************************************************
* a simple hash table implementation
******************************************************/
/*
* Function to identify the right element from the
* linked list.
*/
static int32_t match_key(void *data, void *arg)
{
collections_hash_elem *elem = (collections_hash_elem *) data;
uint64_t key = *((uint64_t *)arg);
return (elem->key == key);
}
/*
* Create a hash table.
*/
static void collections_hash_create_core(collections_hash_table **t, int num_buckets, collections_hash_data_free data_free)
{
int i;
*t = (collections_hash_table *) malloc (sizeof(collections_hash_table));
memset(*t, 0, sizeof(collections_hash_table));
(*t)->num_buckets = num_buckets;
// create a linked list node for each bucket
(*t)->buckets = (collections_listnode **) malloc(sizeof(collections_listnode *) * num_buckets);
for (i = 0; i < num_buckets; i ++) {
collections_list_create(&(*t)->buckets[i], NULL);
}
(*t)->num_elems = 0;
(*t)->data_free = data_free;
// to keep track of traversing the hash table
(*t)->cur_bucket_num = -1;
return;
}
void collections_hash_create(collections_hash_table **t, collections_hash_data_free elem_free)
{
collections_hash_create_core(t, NUM_BUCKETS, elem_free);
}
void collections_hash_create_with_buckets(collections_hash_table **t, int num_buckets, collections_hash_data_free elem_free)
{
collections_hash_create_core(t, num_buckets, elem_free);
}
static int collections_hash_release_elem(void* elem, void * arg)
{
collections_hash_table *t = (collections_hash_table *)arg;
collections_hash_elem *he = (collections_hash_elem *)elem;
if (t->data_free)
{
t->data_free(he->data);
}
free(he);
t->num_elems--;
return 1;
}
// delete the entire hash table
void collections_hash_release(collections_hash_table *t)
{
int bucket_num;
int bucket_size;
collections_listnode *bucket;
for (bucket_num = 0; bucket_num < t->num_buckets; bucket_num ++) {
uint32_t before, after;
bucket = t->buckets[bucket_num];
bucket_size = collections_list_size(bucket);
before = t->num_elems;
collections_list_visit(bucket, collections_hash_release_elem, t);
after = t->num_elems;
assert(before - after == bucket_size);
collections_list_release(bucket);
}
assert(t->num_elems == 0);
free(t->buckets);
free(t);
}
static collections_hash_elem* collections_hash_find_elem(collections_hash_table *t, uint64_t key)
{
uint32_t bucket_num;
collections_listnode *bucket;
collections_hash_elem *elem;
bucket_num = key % t->num_buckets;
bucket = t->buckets[bucket_num];
elem = (collections_hash_elem*) collections_list_find_if(bucket, match_key, &key);
return elem;
}
/*
* Inserts an element into the hash table.
*/
void collections_hash_insert(collections_hash_table *t, uint64_t key, void *data)
{
uint32_t bucket_num;
collections_listnode *bucket;
collections_hash_elem *elem;
elem = collections_hash_find_elem(t, key);
if (elem != NULL) {
printf("Error: key %" PRIu64 " already present in hash table %" PRIu64 "\n",
key, elem->key);
assert(0);
return;
}
bucket_num = key % t->num_buckets;
bucket = t->buckets[bucket_num];
elem = (collections_hash_elem *) malloc(sizeof(collections_hash_elem));
elem->key = key;
elem->data = data;
collections_list_insert(bucket, (void *)elem);
t->num_elems ++;
}
/*
* Retrieves an element from the hash table.
*/
void *collections_hash_find(collections_hash_table *t, uint64_t key)
{
collections_hash_elem *he = collections_hash_find_elem(t, key);
return (he) ? he->data : NULL;
}
/*
* Removes a specific element from the table.
*/
void collections_hash_delete(collections_hash_table *t, uint64_t key)
{
uint32_t bucket_num;
collections_listnode *bucket;
collections_hash_elem *elem;
bucket_num = key % t->num_buckets;
bucket = t->buckets[bucket_num];
elem = (collections_hash_elem*) collections_list_remove_if(bucket, match_key, &key);
if (elem) {
uint32_t n = t->num_elems;
collections_hash_release_elem(elem, t);
assert(1 == n - t->num_elems);
}
else
{
printf("Error: cannot find the node with key %" PRIu64 " in collections_hash_release\n", key);
}
}
/*
* Returns the number of elements in the hash table.
*/
uint32_t collections_hash_size(collections_hash_table *t)
{
return (t->num_elems);
}
static collections_listnode* collections_hash_get_next_valid_bucket(collections_hash_table* t)
{
collections_listnode* bucket;
do {
t->cur_bucket_num ++;
if (t->cur_bucket_num < t->num_buckets) {
if (!t->buckets[t->cur_bucket_num]) {
continue;
}
} else {
return NULL;
}
} while (collections_list_size(t->buckets[t->cur_bucket_num]) <= 0);
bucket = t->buckets[t->cur_bucket_num];
collections_list_traverse_start(bucket);
return bucket;
}
int32_t collections_hash_traverse_start(collections_hash_table *t)
{
if (t->cur_bucket_num != -1) {
// if the cur_bucket_num is valid, a
// traversal is already in progress.
printf("Error: collections_hash_table is already opened for traversal.\n");
return -1;
}
collections_hash_get_next_valid_bucket(t);
return 1;
}
/*
* Returns the next element in the hash table. If
* a valid element is found, the key is set to the
* key of the element. If there is no valid element,
* returns null and key is not modified.
*/
void* collections_hash_traverse_next(collections_hash_table* t, uint64_t *key)
{
if (t->cur_bucket_num == -1) {
// if the cur_bucket_num is invalid,
// hash traversal has not been started.
printf("Error: collections_hash_table must be opened for traversal first.\n");
return NULL;
}
if (t->cur_bucket_num >= t->num_buckets) {
// all the buckets have been traversed.
return NULL;
} else {
collections_listnode* bucket;
collections_hash_elem* ret;
if (t->buckets[t->cur_bucket_num]) {
bucket = t->buckets[t->cur_bucket_num];
ret = (collections_hash_elem*) collections_list_traverse_next(bucket);
if (ret) {
*key = ret->key;
return ret->data;
} else {
// this list traversal is over.
// let's close it.
collections_list_traverse_end(bucket);
}
}
bucket = collections_hash_get_next_valid_bucket(t);
if (!bucket) {
return NULL;
} else {
ret = (collections_hash_elem*) collections_list_traverse_next(bucket);
assert(ret != NULL);
}
*key = ret->key;
return ret->data;
}
}
int32_t collections_hash_traverse_end(collections_hash_table* t)
{
if (t->cur_bucket_num == -1) {
// if the cur_bucket_num is invalid,
// hash traversal has not been started.
printf("Error: collections_hash_table must be opened for traversal first.\n");
return -1;
}
// XXX The bucktes (list) are not reset here which may cause errors when the
// hash table is only traversed half way.
t->cur_bucket_num = -1;
return 1;
}
struct collections_hash_visitor_tuple
{
collections_hash_visitor_func func;
void *arg;
};
static int collections_hash_visit0(void* list_data, void* arg)
{
struct collections_hash_visitor_tuple *t = (struct collections_hash_visitor_tuple *)arg;
collections_hash_elem *he = (collections_hash_elem*)list_data;
return t->func(he->key, he->data, t->arg);
}
int collections_hash_visit(collections_hash_table* t, collections_hash_visitor_func func, void* arg)
{
struct collections_hash_visitor_tuple tuple = { func, arg };
int i = 0;
while (i < t->num_buckets)
{
if (collections_list_visit(t->buckets[i], collections_hash_visit0, &tuple) == 0) {
break;
}
i++;
}
return (i == t->num_buckets);
}