Added hashtable

This commit is contained in:
Arek 2024-11-02 18:37:10 +01:00
parent ac136d4ffb
commit 2a436f1ea5
4 changed files with 303 additions and 110 deletions

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0 ) cmake_minimum_required(VERSION 3.10 )
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
project(wlgol project(wlgol
VERSION 0.0.1 VERSION 0.0.1
@ -22,7 +22,6 @@ add_custom_command(
) )
set(SOURCES set(SOURCES
wlgol.c
tkeyboard.c tkeyboard.c
tpointer.c tpointer.c
hashtable.c hashtable.c
@ -33,7 +32,7 @@ set(GENHEADERS
${CMAKE_CURRENT_BINARY_DIR}/include/xdg-shell.h ${CMAKE_CURRENT_BINARY_DIR}/include/xdg-shell.h
) )
add_executable(wlgol ${SOURCES} ${GENHEADERS}) add_executable(wlgol wlgol.c ${SOURCES} ${GENHEADERS})
target_include_directories(wlgol target_include_directories(wlgol
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include
@ -44,3 +43,20 @@ target_link_libraries(wlgol
wayland-client wayland-client
wayland-cursor wayland-cursor
) )
add_executable(test_hashtable tests/test_hashtable.c
${SOURCES}
)
target_link_libraries(test_hashtable
wayland-client
wayland-cursor
)
target_include_directories(test_hashtable
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
)
enable_testing()
add_test(NAME Hashtable COMMAND test_hashtable)

View File

@ -7,124 +7,193 @@
#include "hashtable.h" #include "hashtable.h"
/**
* @brief Hash function for hashtable // entry in the hashtable
* struct entry
* This function takes a string key and calculates a hash value. {
* The hash value is used to determine the index in the hashtable const char *key;
* where the key-value pair should be stored. void *value;
* struct entry *next;
* @param key The string key to hash };
* @return The calculated hash value
*/ struct htobj
{
int size;
struct entry **table;
};
struct item {
struct entry *prev, *found;
int index;
};
static unsigned int hash(const char *key) static unsigned int hash(const char *key)
{ {
unsigned int hash = 0; // Initialize hash value to 0 unsigned int hash = 0;
// Iterate over each character in the key string
while (*key) while (*key)
{ {
// Calculate the hash value by multiplying the current hash value
// with 32 (2^5) and subtracting the current hash value. Then add the
// ASCII value of the current character.
hash = (hash << 5) - hash + *key++; hash = (hash << 5) - hash + *key++;
} }
// Return the calculated hash value
return hash; return hash;
} }
/** hashtable hashtable_new(int size)
* @brief Create a new hashtable
*
* This function creates a new hashtable with the specified size.
* It initializes the table with NULL entries.
*
* @param size The size of the hashtable
* @return A pointer to the newly created hashtable
*/
hashtable *hashtable_new(int size)
{ {
// Allocate memory for the hashtable // Allocate memory for the hashtable
hashtable *h = malloc(sizeof(hashtable)); hashtable h = malloc(sizeof(struct htobj));
// Set the size of the hashtable // Set the size of the hashtable
h->size = size; h->size = size;
// Allocate memory for the entry pointers // Allocate memory for the entry pointers
h->table = malloc(sizeof(entry *) * size); h->table = calloc(size, sizeof(struct entry *));
// Initialize all entries to NULL
for (int i = 0; i < size; i++)
{
h->table[i] = NULL;
}
// Return the newly created hashtable // Return the newly created hashtable
return h; return h;
} }
// hashtable_insert struct item hashtable_lookup_item(hashtable h, const char *key)
void hashtable_insert(hashtable *h, const char *key, void *value) { {
struct item result = {
.found = NULL,
.index = -1,
};
// Calculate the index using the hash function int index = hash(key) % h->size;
struct entry *e = h->table[index];
struct entry *prev = NULL;
while (e != NULL) {
if (strcmp(e->key, key) == 0) {
result.index = index;
result.found = e;
result.prev = prev;
return result;
}
prev = e;
e = e->next;
}
return result; // empty in this case
}
void *hashtable_set(hashtable h, const char *key, void *value)
{
struct item item = hashtable_lookup_item(h, key);
// not found - insert new
if (item.found == NULL) {
int index = hash(key) % h->size; int index = hash(key) % h->size;
// Create a new entry // Create a new entry
entry *e = malloc(sizeof(entry)); struct entry *e = malloc(sizeof(struct entry));
e->key = key; e->key = strdup(key); // key is the copy
e->value = value; e->value = value;
e->next = NULL; e->next = NULL;
// Insert the entry at the beginning of the linked list // Insert the entry at the beginning of the linked list
e->next = h->table[index]; e->next = h->table[index];
h->table[index] = e; h->table[index] = e;
return NULL;
}
// found replace the value
void *old_value = item.found->value;
item.found-> value = value;
return; return old_value;
} }
void *hashtable_lookup(hashtable h, const char *key)
// hashtable_lookup {
void *hashtable_lookup(hashtable *h, const char *key) { struct item item = hashtable_lookup_item(h, key);
if (item.found != NULL) {
// Calculate the index using the hash function return item.found->value;
int index = hash(key) % h->size;
// Traverse the linked list at the index
entry *e = h->table[index];
while (e != NULL) {
if (strcmp(e->key, key) == 0) {
return e->value;
} }
e = e->next;
}
// Return NULL if the key is not found
return NULL; return NULL;
} }
void *hashtable_remove(hashtable h, const char *key)
// hashtable_remove {
void hashtable_remove(hashtable *h, const char *key) { struct item item = hashtable_lookup_item(h, key);
if (item.found == NULL) {
// Calculate the index using the hash function return NULL;
int index = hash(key) % h->size; }
// found, so detete it
// Traverse the linked list at the index if (item.prev != NULL) {
entry *e = h->table[index]; item.prev->next = item.found->next;
entry *prev = NULL;
while (e != NULL) {
if (strcmp(e->key, key) == 0) {
if (prev == NULL) {
h->table[index] = e->next;
} else { } else {
prev->next = e->next; h->table[item.index] = item.found->next;
} }
free(e); // free unused memory
return; void *value = item.found->value; // remember value
free((void*) item.found->key); // free key
free((void*) item.found); // free entry
return value;
} }
prev = e;
void hashtable_destroy_bucket(hashtable h, int i)
{
struct entry *e = h->table[i];
while (e != NULL) {
free((void*)e->key);
e = e->next; e = e->next;
free(e);
} }
} }
void hashtable_destroy(hashtable h)
{
if (h==NULL) return;
for (int i = 0; i < h->size; i++) {
hashtable_destroy_bucket(h, i);
}
free (h->table);
free(h);
}
ht_iterator hashtable_iter(hashtable h)
{
ht_iterator iter = {
.h = h,
.index = -1,
.entry = NULL,
};
return iter;
}
bool ht_iter_next(ht_iterator *iter)
{
if (iter == NULL) {
return false;
}
hashtable h = iter->h;
while(true) {
if (iter->index >= h->size) {
return false;
}
if (iter->entry == NULL) {
iter->index++;
iter->entry = h->table[iter->index];
} else {
iter->entry = iter->entry->next;
}
if (iter->entry != NULL) return true;
}
}
const char *ht_iter_key(ht_iterator *iter)
{
if (iter == NULL || iter->entry == NULL) return NULL;
return iter->entry->key;
}
void *ht_iter_value(ht_iterator *iter)
{
if (iter == NULL || iter->entry == NULL) return NULL;
return iter->entry->value;
}

View File

@ -1,23 +1,16 @@
#ifndef HASHTABLE_H #ifndef HASHTABLE_H
#define HASHTABLE_H #define HASHTABLE_H
#include <stdlib.h> #include <stdbool.h>
#include <string.h>
// entry in the hashtable struct htobj;
typedef struct entry typedef struct htobj *hashtable;
{
const char *key;
void *value;
struct entry *next;
} entry;
// hashtable typedef struct {
typedef struct struct htobj *h;
{ struct entry *entry;
int size; int index;
entry **table; } ht_iterator;
} hashtable;
/** /**
* @brief Create a new hashtable * @brief Create a new hashtable
@ -28,7 +21,7 @@ typedef struct
* @param size The size of the hashtable * @param size The size of the hashtable
* @return A pointer to the newly created hashtable * @return A pointer to the newly created hashtable
*/ */
hashtable *hashtable_new(int size); hashtable hashtable_new(int size);
/** /**
* @brief Insert a new key-value pair into the hashtable * @brief Insert a new key-value pair into the hashtable
@ -40,8 +33,9 @@ hashtable *hashtable_new(int size);
* @param h Pointer to the hashtable * @param h Pointer to the hashtable
* @param key Pointer to the key * @param key Pointer to the key
* @param value Pointer to the value * @param value Pointer to the value
* @return null or pointer to the previous value of the key
*/ */
void hashtable_insert(hashtable *h, const char *key, void *value); void *hashtable_set(hashtable h, const char *key, void *value);
/** /**
* @brief Get the value associated with a key from the hashtable * @brief Get the value associated with a key from the hashtable
@ -54,7 +48,7 @@ void hashtable_insert(hashtable *h, const char *key, void *value);
* @param key Pointer to the key * @param key Pointer to the key
* @return Pointer to the value associated with the key, or NULL if not found * @return Pointer to the value associated with the key, or NULL if not found
*/ */
void *hashtable_lookup(hashtable *h, const char *key); void *hashtable_lookup(hashtable h, const char *key);
/** /**
* @brief Remove a key-value pair from the hashtable * @brief Remove a key-value pair from the hashtable
@ -66,6 +60,57 @@ void *hashtable_lookup(hashtable *h, const char *key);
* *
* @param h Pointer to the hashtable * @param h Pointer to the hashtable
* @param key Pointer to the key to remove * @param key Pointer to the key to remove
* @return null or value associated with the removed key.
*/ */
void hashtable_remove(hashtable *h, const char *key); void *hashtable_remove(hashtable h, const char *key);
/**
* @brief Destroy hashtable.
*
* Values must be destroyed by the caller earlier.
*
* @param h Reference to the hastable.
*/
void hashtable_destroy(hashtable h);
/**
* @brief Get the iterator of key-value pairs in the hashtable
*
* This function returns the iterator of key-value pairs in the hashtable.
*
* @param h Pointer to the hashtable
* @return The iterator of key-value pairs in the hashtable
*/
ht_iterator hashtable_iter(hashtable h);
/**
* @brief Get the next key-value pair from the iterator
*
* This function returns the next key-value pair from the iterator.
*
* @param iter Pointer to the iterator
* @return true if there is a next key-value pair, false otherwise
*/
bool ht_iter_next(ht_iterator *iter);
/**
* @brief Get the key from the iterator
*
* This function returns the key from the iterator.
*
* @param iter Pointer to the iterator
* @return The key from the iterator
*/
const char *ht_iter_key(ht_iterator *iter);
/**
* @brief Get the value from the iterator
*
* This function returns the value from the iterator.
*
* @param iter Pointer to the iterator
* @return The value from the iterator
*/
void *ht_iter_value(ht_iterator *iter);
#endif #endif

63
tests/test_hashtable.c Normal file
View File

@ -0,0 +1,63 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <hashtable.h>
void test_hashtable(int size) {
hashtable h = hashtable_new(size);
void *value1 = "to jest jakaś tam wartość";
void *value2 = "I inna";
void *value3 = "trzecia";
void *value4 = "Czwarta";
void *value5 = "Piąta";
void *retval = hashtable_set(h, "jeden", value1);
assert(retval == NULL);
void *found = hashtable_lookup(h, "jeden");
assert(found != NULL && !strcmp(value1, found));
retval = hashtable_set(h, "dwa", value2);
assert(retval == NULL);
retval = hashtable_set(h, "trzy", value3);
assert(retval == NULL);
retval = hashtable_set(h, "cztery", value4);
assert(retval == NULL);
retval = hashtable_set(h, "jeden", value5);
assert(retval != NULL);
assert (retval == value1);
retval = hashtable_lookup(h, "niema");
assert(retval == NULL);
retval = hashtable_lookup(h, "jeden");
assert(retval == value5);
ht_iterator iter = hashtable_iter(h);
while (ht_iter_next(&iter)) {
printf("Key %s, value: %s\n", ht_iter_key(&iter), (const char*) ht_iter_value(&iter));
}
retval = hashtable_remove(h, "cztery");
assert (retval == value4);
retval = hashtable_remove(h, "trzy");
assert(retval == value3);
retval = hashtable_remove(h, "dwa");
assert(retval == value2);
retval = hashtable_remove(h, "jeden");
assert(retval == value5);
hashtable_destroy(h);
}
int main() {
test_hashtable(2);
test_hashtable(10);
}