314 lines
8.0 KiB
C
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);
|
|
}
|