diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ac2f99..3124799 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0 ) +cmake_minimum_required(VERSION 3.10 ) set(CMAKE_C_STANDARD 11) project(wlgol VERSION 0.0.1 @@ -22,7 +22,6 @@ add_custom_command( ) set(SOURCES - wlgol.c tkeyboard.c tpointer.c hashtable.c @@ -33,7 +32,7 @@ set(GENHEADERS ${CMAKE_CURRENT_BINARY_DIR}/include/xdg-shell.h ) -add_executable(wlgol ${SOURCES} ${GENHEADERS}) +add_executable(wlgol wlgol.c ${SOURCES} ${GENHEADERS}) target_include_directories(wlgol PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include @@ -44,3 +43,20 @@ target_link_libraries(wlgol wayland-client 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) diff --git a/hashtable.c b/hashtable.c index 4134e0c..24ce993 100644 --- a/hashtable.c +++ b/hashtable.c @@ -7,124 +7,193 @@ #include "hashtable.h" -/** - * @brief Hash function for hashtable - * - * This function takes a string key and calculates a hash value. - * The hash value is used to determine the index in the hashtable - * where the key-value pair should be stored. - * - * @param key The string key to hash - * @return The calculated hash value - */ + +// entry in the hashtable +struct entry +{ + const char *key; + void *value; + struct entry *next; +}; + +struct htobj +{ + int size; + struct entry **table; +}; + +struct item { + struct entry *prev, *found; + int index; +}; + 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) { - // 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++; } - - // Return the calculated hash value return hash; } -/** - * @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) +hashtable hashtable_new(int size) { // Allocate memory for the hashtable - hashtable *h = malloc(sizeof(hashtable)); + hashtable h = malloc(sizeof(struct htobj)); // Set the size of the hashtable h->size = size; // Allocate memory for the entry pointers - h->table = malloc(sizeof(entry *) * size); - - // Initialize all entries to NULL - for (int i = 0; i < size; i++) - { - h->table[i] = NULL; - } + h->table = calloc(size, sizeof(struct entry *)); // Return the newly created hashtable return h; } -// hashtable_insert -void hashtable_insert(hashtable *h, const char *key, void *value) { +struct item hashtable_lookup_item(hashtable h, const char *key) +{ + 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; - // Create a new entry - entry *e = malloc(sizeof(entry)); - e->key = key; - e->value = value; - e->next = NULL; - - // Insert the entry at the beginning of the linked list - e->next = h->table[index]; - h->table[index] = e; - - return; -} - - -// hashtable_lookup -void *hashtable_lookup(hashtable *h, const char *key) { - - // Calculate the index using the hash function - 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; -} - - -// hashtable_remove -void hashtable_remove(hashtable *h, const char *key) { - - // Calculate the index using the hash function - int index = hash(key) % h->size; - - // Traverse the linked list at the index - entry *e = h->table[index]; - entry *prev = NULL; - while (e != NULL) { - if (strcmp(e->key, key) == 0) { - if (prev == NULL) { - h->table[index] = e->next; - } else { - prev->next = e->next; - } - free(e); - return; + 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; + + // Create a new entry + struct entry *e = malloc(sizeof(struct entry)); + e->key = strdup(key); // key is the copy + e->value = value; + e->next = NULL; + + // Insert the entry at the beginning of the linked list + e->next = h->table[index]; + h->table[index] = e; + return NULL; + } + // found replace the value + void *old_value = item.found->value; + item.found-> value = value; + + return old_value; +} + +void *hashtable_lookup(hashtable h, const char *key) +{ + struct item item = hashtable_lookup_item(h, key); + if (item.found != NULL) { + return item.found->value; + } + return NULL; +} + +void *hashtable_remove(hashtable h, const char *key) +{ + struct item item = hashtable_lookup_item(h, key); + if (item.found == NULL) { + return NULL; + } + // found, so detete it + if (item.prev != NULL) { + item.prev->next = item.found->next; + } else { + h->table[item.index] = item.found->next; + } + // free unused memory + void *value = item.found->value; // remember value + free((void*) item.found->key); // free key + free((void*) item.found); // free entry + + return value; +} + +void hashtable_destroy_bucket(hashtable h, int i) +{ + struct entry *e = h->table[i]; + while (e != NULL) { + free((void*)e->key); + 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; } diff --git a/include/hashtable.h b/include/hashtable.h index 2fc59ea..7e374a2 100644 --- a/include/hashtable.h +++ b/include/hashtable.h @@ -1,23 +1,16 @@ #ifndef HASHTABLE_H #define HASHTABLE_H -#include -#include +#include -// entry in the hashtable -typedef struct entry -{ - const char *key; - void *value; - struct entry *next; -} entry; +struct htobj; +typedef struct htobj *hashtable; -// hashtable -typedef struct -{ - int size; - entry **table; -} hashtable; +typedef struct { + struct htobj *h; + struct entry *entry; + int index; +} ht_iterator; /** * @brief Create a new hashtable @@ -28,7 +21,7 @@ typedef struct * @param size The size of the 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 @@ -40,8 +33,9 @@ hashtable *hashtable_new(int size); * @param h Pointer to the hashtable * @param key Pointer to the key * @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 @@ -54,7 +48,7 @@ void hashtable_insert(hashtable *h, const char *key, void *value); * @param key Pointer to the key * @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 @@ -66,6 +60,57 @@ void *hashtable_lookup(hashtable *h, const char *key); * * @param h Pointer to the hashtable * @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 diff --git a/tests/test_hashtable.c b/tests/test_hashtable.c new file mode 100644 index 0000000..286bf67 --- /dev/null +++ b/tests/test_hashtable.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include + +#include + +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); +}