Wrote a naive hashtable.

Be careful regarding memeory handling using this table. It takes
pointers as values. Deletes the pointers on occasion and destruction but
doesn't copy the original pointer. So pointers inserted should be left
to be handled by the table.
This commit is contained in:
Linus Probert 2017-12-12 13:06:01 +01:00
parent ac63f0b172
commit 86eb3aee01
5 changed files with 283 additions and 0 deletions

View File

@ -7,6 +7,7 @@ project(breakhack C)
include(FindLua) include(FindLua)
add_subdirectory(linkedlist) add_subdirectory(linkedlist)
add_subdirectory(hashtable)
include_directories(linkedlist include_directories(linkedlist
${LUA_INCLUDE_DIR} ${LUA_INCLUDE_DIR}
@ -32,6 +33,7 @@ add_executable(breakhack
target_link_libraries(breakhack target_link_libraries(breakhack
linkedlist linkedlist
hashtable
${LUA_LIBRARIES} ${LUA_LIBRARIES}
-lSDL2 -lSDL2
-lSDL2_image -lSDL2_image

15
hashtable/CMakeLists.txt Normal file
View File

@ -0,0 +1,15 @@
cmake_minimum_required (VERSION 3.2.0)
SET(CMAKE_COLOR_MAKEFILE ON)
project(hashtable C)
add_definitions("-Wall")
add_library(hashtable hashtable.c)
enable_testing()
add_executable(test_table EXCLUDE_FROM_ALL check_hashtable hashtable)
target_compile_options(test_table PRIVATE -pthread)
target_link_libraries(test_table -lcheck)
add_test(test_table test_table)

View File

@ -0,0 +1,92 @@
#include <check.h>
#include <stdlib.h>
#include "hashtable.h"
START_TEST(test_hashtable_create)
{
Hashtable *table = ht_create(10);
ck_assert( table->size == 10 );
ck_assert( table != NULL );
ht_destroy(table);
}
END_TEST
START_TEST(test_hashtable_set_get)
{
int i;
int* values0[10];
int* values1[10];
char* keys[] = {
"key0",
"key1",
"key2",
"key3",
"key4",
"key5",
"key6",
"key7",
"key8",
"key9",
};
for (i = 0; i < 10; ++i) {
values0[i] = malloc(sizeof(int));
*values0[i] = i;
values1[i] = malloc(sizeof(int));
*values1[i] = 9-i;
}
Hashtable *table = ht_create(10);
for (i = 0; i < 10; ++i) {
ht_set(table, keys[i], values0[i]);
}
for (i = 0; i < 10; ++i) {
ck_assert( *values0[i] == *((int*) ht_get(table, keys[i])) );
}
for (i = 0; i < 10; ++i) {
ht_set(table, keys[i], values1[i]);
}
for (i = 0; i < 10; ++i) {
ck_assert( *values1[i] == *((int*) ht_get(table, keys[i])) );
}
ht_destroy(table);
}
END_TEST
Suite* t_suite_create()
{
Suite *s;
TCase *tc_core;
s = suite_create("Hashtable");
tc_core = tcase_create("Core");
tcase_add_test(tc_core, test_hashtable_create);
tcase_add_test(tc_core, test_hashtable_set_get);
suite_add_tcase(s, tc_core);
return s;
}
int main(void)
{
int number_failed;
Suite *s;
SRunner *sr;
s = t_suite_create();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return number_failed == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

151
hashtable/hashtable.c Normal file
View File

@ -0,0 +1,151 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "hashtable.h"
static void*
ec_malloc(unsigned int size)
{
void *ptr = malloc(size);
if (ptr == NULL) {
fprintf(stderr, "[!!] Failed to allocate hashtable\n");
exit(-1);
}
return ptr;
}
Hashtable*
ht_create(unsigned int size)
{
Hashtable *table;
unsigned int i;
if (size < 1)
return NULL;
table = ec_malloc(sizeof(Hashtable));
table->size = size;
table->entries = ec_malloc(sizeof(Entry) * size);
for (i = 0; i < size; ++i) {
table->entries[i] = NULL;
}
return table;
}
static unsigned int
hash(Hashtable *table, char *key)
{
unsigned long int hashval = 0;
int i = 0;
while (hashval < ULONG_MAX && i < strlen(key)) {
hashval = hashval << 8;
hashval += key[i++];
}
return hashval % table->size;
}
static Entry*
entry_create(char *key, void *value)
{
Entry *entry;
entry = ec_malloc(sizeof(Entry));
entry->key = strdup(key);
entry->value = value;
entry->next = NULL;
return entry;
}
void
ht_set(Hashtable *table, char *key, void *val)
{
int hashkey = 0;
Entry *newEntry;
Entry *next;
Entry *last;
hashkey = hash(table, key);
next = table->entries[hashkey];
/* Find a position */
while (next != NULL
&& next->key != NULL
&& strcmp(key, next->key) > 0)
{
last = next;
next = next->next;
}
if (next && next->key && strcmp(key, next->key) == 0) {
/* Collision */
free(next->value);
next->value = val;
} else {
/* New entry */
newEntry = entry_create(key, val);
if (next == table->entries[hashkey]) {
table->entries[hashkey] = newEntry;
newEntry->next = next;
} else if(next == NULL) {
last->next = newEntry;
} else {
newEntry->next = next;
last->next = newEntry;
}
}
}
void*
ht_get(Hashtable *table, char *key)
{
int hashkey = 0;
Entry *entry;
hashkey = hash(table, key);
entry = table->entries[hashkey];
while (entry && entry->key && strcmp(entry->key, key) > 0) {
entry = entry->next;
}
if (!entry || !entry->key || strcmp(entry->key, key) != 0) {
return NULL;
}
return entry->value;
}
void
ht_destroy(Hashtable *table)
{
Entry *entry, *next;
unsigned int i;
if (table == NULL) {
return;
}
for (i = 0; i < table->size; ++i) {
entry = table->entries[i];
if (entry == NULL)
continue;
while (entry) {
next = entry->next;
free(entry->value);
entry->value = NULL;
free(entry);
entry = next;
}
}
free(table);
}

23
hashtable/hashtable.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef HASHTABLE_H_
#define HASHTABLE_H_
typedef struct entry_t {
char *key;
void *value;
struct entry_t *next;
} Entry;
typedef struct table_t {
unsigned int size;
Entry **entries;
} Hashtable;
Hashtable* ht_create(unsigned int size);
void ht_set(Hashtable*, char *key, void *val);
void* ht_get(Hashtable*, char *key);
void ht_destroy(Hashtable*);
#endif // HASHTABLE_H_