Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
321 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/******************************************************************************* | ||
* File : chainedhashtable.h | ||
* Author(s) : Tekin Ozbek <tekin@tekinozbek.com> | ||
******************************************************************************/ | ||
|
||
#ifndef ODS_CHAINEDHASHTABLE_H_ | ||
#define ODS_CHAINEDHASHTABLE_H_ | ||
|
||
#include <stdlib.h> | ||
|
||
#include <dllist.h> | ||
|
||
typedef struct chainedhashtable_t { | ||
|
||
dllist_t* array; | ||
|
||
size_t length; | ||
size_t elem_size; | ||
size_t w; | ||
size_t d; | ||
int z; | ||
|
||
int (*hashcode)(void *); | ||
|
||
} chainedhashtable_t; | ||
|
||
/* FUNCTION | ||
* chainedhashtable_add | ||
* | ||
* DESCRIPTION | ||
* Adds an element if it's not in the table already. | ||
* | ||
* PARAMETERS | ||
* table A valid pointer to an initialized chainedhashtable_t struct. | ||
* elem The element that will be copied into the table. | ||
* | ||
* RETURN VALUES | ||
* Returns 1 if added successfully, 0 otherwise (it already exists in the | ||
* table). | ||
*/ | ||
extern int chainedhashtable_add(chainedhashtable_t* table, | ||
void* elem); | ||
|
||
/* FUNCTION | ||
* chainedhashtable_clear | ||
* | ||
* DESCRIPTION | ||
* Clears the table, reduces the size of the table array. | ||
* | ||
* PARAMETERS | ||
* table A valid pointer to an initialized chainedhashtable_t struct. | ||
*/ | ||
extern void chainedhashtable_clear(chainedhashtable_t* table); | ||
|
||
/* FUNCTION | ||
* chainedhashtable_dispose | ||
* | ||
* DESCRIPTION | ||
* Cleans up initialized lists and deallocates all memory. | ||
* | ||
* PARAMETERS | ||
* table A valid pointer to an initialized chainedhashtable_t struct. | ||
*/ | ||
extern void chainedhashtable_dispose(chainedhashtable_t* table); | ||
|
||
/* FUNCTION | ||
* chainedhashtable_find | ||
* | ||
* DESCRIPTION | ||
* Searches for an element in the hash table. | ||
* | ||
* PARAMETERS | ||
* table A valid pointer to an initialized chainedhashtable_t struct. | ||
* elem The element that will be searched for in the table. | ||
* | ||
* RETURN VALUES | ||
* Returns 1 if the element was found, 0 otherwise. | ||
*/ | ||
extern int chainedhashtable_find(chainedhashtable_t* table, | ||
void* elem); | ||
|
||
/* FUNCTION | ||
* chainedhashtable_init | ||
* | ||
* DESCRIPTION | ||
* Initializes a chainedhashtable_t struct. | ||
* | ||
* PARAMETERS | ||
* table A valid pointer to a chainedhashtable_t struct. | ||
* elem_size Size of the elements that will be stored in the table. | ||
* hashcode Pointer to a hash function. This function must take a void * | ||
* as a parameter and return an int. The better the function, | ||
* the more efficient the table will be. Cannot be null. | ||
* random Pointer to a random function that takes no parameters and | ||
* returns an int value. If null, the default rand() from the | ||
* standard library is used. | ||
* | ||
* RETURN VALUES | ||
* Returns 1 if the element was found, 0 otherwise. | ||
*/ | ||
extern void chainedhashtable_init(chainedhashtable_t *table, | ||
size_t elem_size, | ||
int (*hashcode)(void *), | ||
int (*random)(void)); | ||
|
||
/* FUNCTION | ||
* chainedhashtable_remove | ||
* | ||
* DESCRIPTION | ||
* Removes an element from the table. | ||
* | ||
* PARAMETERS | ||
* table A valid pointer to an initialized chainedhashtable_t struct. | ||
* elem The element that will be searched for in the table. | ||
*/ | ||
extern void chainedhashtable_remove(chainedhashtable_t* table, | ||
void* elem); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
/******************************************************************************* | ||
* File : chainedhashtable.c | ||
* Author(s) : Tekin Ozbek <tekin@tekinozbek.com> | ||
******************************************************************************/ | ||
|
||
#include <string.h> | ||
#include <assert.h> | ||
#include <limits.h> | ||
#include <stdlib.h> | ||
#include <stdio.h> | ||
|
||
#include <chainedhashtable.h> | ||
#include <dllist.h> | ||
#include <iterator.h> | ||
|
||
static size_t hash(chainedhashtable_t* table, int elem_hashcode) { | ||
|
||
return (unsigned int)(table->z * elem_hashcode) >> (table->w - table->d); | ||
} | ||
|
||
static dllist_t* allocate_table(chainedhashtable_t* table) { | ||
|
||
dllist_t* dllist_array; | ||
size_t i; | ||
size_t size = 1 << table->d; | ||
|
||
/* allocate space and initialize each dllist */ | ||
dllist_array = malloc(sizeof(dllist_t) * size); | ||
assert((void *)dllist_array != NULL); | ||
|
||
for (i = 0; i < size; ++i) | ||
dllist_init(dllist_array + i, table->elem_size); | ||
|
||
return dllist_array; | ||
} | ||
|
||
static void resize(chainedhashtable_t* table) { | ||
|
||
dllist_t* list; | ||
dllist_t* old_array = table->array; | ||
size_t old_array_size = 1 << table->d; | ||
iterator_t it; | ||
size_t i; | ||
|
||
/* determine the new size */ | ||
table->d = 1; | ||
while ((1 << table->d) <= table->length) | ||
++table->d; | ||
|
||
/* allocate a new array for this table */ | ||
table->array = allocate_table(table); | ||
|
||
/* iterate over the old array and copy elements */ | ||
for (i = 0; i < old_array_size; ++i) { | ||
|
||
list = old_array + i; | ||
|
||
if (list->length > 0) { | ||
|
||
dllist_iterator(list, &it, 0, list->length - 1); | ||
while (it.next(&it)) | ||
chainedhashtable_add(table, it.elem(&it)); | ||
|
||
it.dispose(&it); | ||
} | ||
|
||
dllist_dispose(list); | ||
} | ||
|
||
free(old_array); | ||
} | ||
|
||
int chainedhashtable_add(chainedhashtable_t* table, void* elem) { | ||
|
||
dllist_t* list; | ||
|
||
assert((void *)table != NULL); | ||
assert(elem != NULL); | ||
|
||
/* check if element exist, return false if so */ | ||
if (chainedhashtable_find(table, elem)) | ||
return 0; | ||
|
||
if (table->length + 1 > (1 << table->d)) | ||
resize(table); | ||
|
||
/* find which list we are supposed to insert the element */ | ||
list = table->array + hash(table, table->hashcode(elem)); | ||
|
||
/* add the element */ | ||
dllist_add(list, list->length, elem); | ||
++table->length; | ||
|
||
return 1; | ||
} | ||
|
||
void chainedhashtable_clear(chainedhashtable_t* table) { | ||
|
||
assert((void *)table != NULL); | ||
|
||
table->d = 1; | ||
table->length = 0; | ||
table->array = allocate_table(table); | ||
} | ||
|
||
void chainedhashtable_dispose(chainedhashtable_t* table) { | ||
|
||
size_t i; | ||
size_t array_size; | ||
|
||
assert((void *)table != NULL); | ||
|
||
array_size = 1 << table->d; | ||
|
||
/* iterate over the array of lists and dispose each */ | ||
for (i = 0; i < array_size; ++i) | ||
dllist_dispose(table->array + i); | ||
|
||
free(table->array); | ||
} | ||
|
||
int chainedhashtable_find(chainedhashtable_t* table, void* elem) { | ||
|
||
dllist_t* list; | ||
iterator_t it; | ||
|
||
assert((void *)table != NULL); | ||
assert(elem != NULL); | ||
|
||
list = table->array + hash(table, table->hashcode(elem)); | ||
|
||
/* find the element in the list */ | ||
if (list->length > 0) { | ||
|
||
dllist_iterator(list, &it, 0, list->length - 1); | ||
while (it.next(&it)) { | ||
|
||
if (!memcmp(elem, it.elem(&it), table->elem_size)){ | ||
|
||
it.dispose(&it); | ||
return 1; | ||
} | ||
} | ||
|
||
it.dispose(&it); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
void chainedhashtable_init(chainedhashtable_t* table, size_t elem_size, | ||
int (*hashcode)(void *), int (*random)(void)) { | ||
|
||
assert((void *)table != NULL); | ||
assert(hashcode != NULL); | ||
assert(elem_size > 0); | ||
|
||
table->elem_size = elem_size; | ||
table->w = sizeof(int) * CHAR_BIT; | ||
table->d = 1; | ||
table->hashcode = hashcode; | ||
table->length = 0; | ||
|
||
/* generate a random odd integer z */ | ||
table->z = (random != NULL ? random() : rand()) | 1; | ||
|
||
table->array = allocate_table(table); | ||
} | ||
|
||
void chainedhashtable_remove(chainedhashtable_t* table, void* elem) { | ||
|
||
dllist_t* list; | ||
iterator_t it; | ||
size_t i; | ||
|
||
assert((void *)table != NULL); | ||
assert(elem != NULL); | ||
|
||
/* find the list where the element should be */ | ||
list = table->array + hash(table, table->hashcode(elem)); | ||
|
||
/* look for the element in the list */ | ||
if (list->length > 0) { | ||
|
||
i = 0; | ||
|
||
dllist_iterator(list, &it, 0, list->length - 1); | ||
while (it.next(&it)) { | ||
|
||
if (!memcmp(it.elem(&it), elem, table->elem_size)) { | ||
|
||
it.dispose(&it); | ||
dllist_remove(list, i, NULL); | ||
return; | ||
} | ||
|
||
i++; | ||
} | ||
} | ||
} |