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:
parent
ac63f0b172
commit
86eb3aee01
|
@ -7,6 +7,7 @@ project(breakhack C)
|
|||
include(FindLua)
|
||||
|
||||
add_subdirectory(linkedlist)
|
||||
add_subdirectory(hashtable)
|
||||
|
||||
include_directories(linkedlist
|
||||
${LUA_INCLUDE_DIR}
|
||||
|
@ -32,6 +33,7 @@ add_executable(breakhack
|
|||
|
||||
target_link_libraries(breakhack
|
||||
linkedlist
|
||||
hashtable
|
||||
${LUA_LIBRARIES}
|
||||
-lSDL2
|
||||
-lSDL2_image
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
Loading…
Reference in New Issue