From 219812f20e792e61ee7515315f3e0e43e0576ccf Mon Sep 17 00:00:00 2001 From: Rick Barenthin Date: Wed, 27 Jul 2022 19:10:09 +0200 Subject: [PATCH] feat(hashtable): add a hashtable implementation This is a generic hashtable implementation. Reviewed-on: https://git.riba-interactive.de/rick/waitui/pulls/12 PR #12 --- CMakeLists.txt | 2 + app/include/waitui/version.h.in | 2 +- app/project-meta-info.in | 2 +- app/src/main.c | 2 +- library/hashtable/CMakeLists.txt | 22 ++ library/hashtable/include/waitui/hashtable.h | 185 ++++++++++ .../include/waitui/hashtable_generic.h | 118 +++++++ .../include/waitui/hashtable_generic_impl.h | 113 ++++++ library/hashtable/project-meta-info.in | 3 + library/hashtable/src/hashtable.c | 215 ++++++++++++ library/list/CMakeLists.txt | 2 +- library/list/include/waitui/list.h | 2 +- library/list/include/waitui/list_generic.h | 2 +- .../list/include/waitui/list_generic_impl.h | 2 +- library/list/src/list.c | 2 +- library/log/include/waitui/log.h | 44 +-- library/log/src/log.c | 28 +- library/str/CMakeLists.txt | 20 ++ library/str/include/waitui/str.h | 113 ++++++ library/str/project-meta-info.in | 3 + library/str/src/str.c | 8 + tests/CMakeLists.txt | 5 +- tests/library/hashtable/CMakeLists.txt | 15 + tests/library/hashtable/test_hashtable.c | 321 ++++++++++++++++++ tests/library/list/test_list.c | 4 +- 25 files changed, 1186 insertions(+), 49 deletions(-) create mode 100644 library/hashtable/CMakeLists.txt create mode 100644 library/hashtable/include/waitui/hashtable.h create mode 100644 library/hashtable/include/waitui/hashtable_generic.h create mode 100644 library/hashtable/include/waitui/hashtable_generic_impl.h create mode 100644 library/hashtable/project-meta-info.in create mode 100644 library/hashtable/src/hashtable.c create mode 100644 library/str/CMakeLists.txt create mode 100644 library/str/include/waitui/str.h create mode 100644 library/str/project-meta-info.in create mode 100644 library/str/src/str.c create mode 100644 tests/library/hashtable/CMakeLists.txt create mode 100644 tests/library/hashtable/test_hashtable.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5023cf5..bdd63ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,10 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) endif () add_subdirectory(app) +add_subdirectory(library/hashtable) add_subdirectory(library/list) add_subdirectory(library/log) +add_subdirectory(library/str) if ((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR MODERN_CMAKE_BUILD_TESTING) AND BUILD_TESTING) diff --git a/app/include/waitui/version.h.in b/app/include/waitui/version.h.in index 572ea5b..e4c6124 100644 --- a/app/include/waitui/version.h.in +++ b/app/include/waitui/version.h.in @@ -33,4 +33,4 @@ #define WAITUI_VERSION_LONG @PROJECT_VERSION_LONG@L -#endif //WAITUI_VERSION_H \ No newline at end of file +#endif //WAITUI_VERSION_H diff --git a/app/project-meta-info.in b/app/project-meta-info.in index 89c0b67..97edc46 100644 --- a/app/project-meta-info.in +++ b/app/project-meta-info.in @@ -1,4 +1,4 @@ -set(project_version 0.0.1) +set(project_version 0.1.1) set(project_description "waitui executable") set(project_homepage "http://example.com") set(project_prerelease "") \ No newline at end of file diff --git a/app/src/main.c b/app/src/main.c index b556a0b..d0b7e25 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -160,4 +160,4 @@ int main(int argc, char **argv) { waitui_log_debug("waitui execution done"); return result; -} \ No newline at end of file +} diff --git a/library/hashtable/CMakeLists.txt b/library/hashtable/CMakeLists.txt new file mode 100644 index 0000000..e9d37b8 --- /dev/null +++ b/library/hashtable/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) + +include("project-meta-info.in") + +project(waitui-hashtable + VERSION ${project_version} + DESCRIPTION ${project_description} + HOMEPAGE_URL ${project_homepage} + LANGUAGES C) + +add_library(hashtable OBJECT) + +target_sources(hashtable + PRIVATE + "src/hashtable.c" + PUBLIC + "include/waitui/hashtable.h" + ) + +target_include_directories(hashtable PUBLIC "include") + +target_link_libraries(hashtable PUBLIC str) \ No newline at end of file diff --git a/library/hashtable/include/waitui/hashtable.h b/library/hashtable/include/waitui/hashtable.h new file mode 100644 index 0000000..816b486 --- /dev/null +++ b/library/hashtable/include/waitui/hashtable.h @@ -0,0 +1,185 @@ +/** + * @file hashtable.h + * @author rick + * @date 23.07.20 + * @brief File for the HashTable implementation + */ + +#ifndef WAITUI_HASHTABLE_H +#define WAITUI_HASHTABLE_H + +#include + + +// ----------------------------------------------------------------------------- +// Public types +// ----------------------------------------------------------------------------- + +/** + * @brief Type representing a HashTable node + */ +typedef struct waitui_hashtable_node waitui_hashtable_node; +struct waitui_hashtable_node { + str key; + void *value; + int isStolen; + waitui_hashtable_node *next; +}; + +/** + * @brief Type for value destroy function + */ +typedef void (*waitui_hashtable_value_destroy_fn)(void **value); + +/** + * @brief Type for value checking function + */ +typedef int (*waitui_hashtable_value_check_fn)(void *value, void *arg); + +/** + * @brief Type representing a HashTable + */ +typedef struct waitui_hashtable { + waitui_hashtable_node **list; + waitui_hashtable_value_destroy_fn valueDestroyFn; + unsigned long int size; +} waitui_hashtable; + + +// ----------------------------------------------------------------------------- +// Public functions +// ----------------------------------------------------------------------------- + +/** + * @brief Create a HashTable + * @param[in] size The HashTable size + * @param[in] valueDestroyFn Function to call for value destruction + * @return A pointer to waitui_hashtable or NULL if memory allocation failed + */ +extern waitui_hashtable * +waitui_hashtable_new(unsigned long int size, + waitui_hashtable_value_destroy_fn valueDestroyFn); + +/** + * @brief Destroy a HashTable + * @param[in,out] this The HashTable to destroy + * @note This will free call for every value the valueDestroyFn + */ +extern void waitui_hashtable_destroy(waitui_hashtable **this); + +/** + * @brief Insert a value for the key into the HashTable + * @param[in,out] this The HashTable to insert the value + * @param[in] key The key to insert the value for + * @param[in] value The value to insert + * @param[in] valueCheckFn The value checking function to call + * @param[in] arg The value for the second parameter to the valueCheckFn + * @retval 1 Ok + * @retval 0 Memory allocation failed or key already exists + */ +extern int +waitui_hashtable_insertCheck(waitui_hashtable *this, str key, void *value, + waitui_hashtable_value_check_fn valueCheckFn, + void *arg); + +/** + * @brief Search for the key inside the HashTable + * @param[in] this The HashTable to lookup the key + * @param[in] key The key to search for in the HashTable + * @param[in] valueCheckFn The value checking function to call + * @param[in] arg The value for the second parameter to the valueCheckFn + * @return The pointer to the value or NULL if not found + */ +extern void * +waitui_hashtable_lookupCheck(waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckFn, + void *arg); + +/** + * @brief Test whether the HashTable has the key + * @param[in] this The HashTable to check for the key + * @param[in] key The key to look for in the HashTable + * @param[in] valueCheckCallback The value checking function to call + * @param[in] arg The value for the second parameter to the valueCheckCallback + * @retval 1 The HashTable has the key + * @retval 0 The HashTable does not have the key + */ +extern int +waitui_hashtable_hasCheck(waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckCallback, + void *arg); + +/** + * @brief Mark value for key as stolen in the HashTable + * @param[in] this The HashTable to check for the key + * @param[in] key The key to look for in the HashTable + * @param[in] valueCheckFn The value checking function to call + * @param[in] arg The value for the second parameter to the valueCheckFn + * @retval 1 The HashTable has the key + * @retval 0 The HashTable does not have the key + */ +extern int +waitui_hashtable_markStolenCheck(waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckFn, + void *arg); +/** + * @brief Tests whether the value for key is marked as stolen in the HashTable + * @param[in] this The HashTable to check for the key + * @param[in] key The key to look for in the HashTable + * @param[in] valueCheckFn The value checking function to call + * @param[in] arg The value for the second parameter to the valueCheckFn + * @retval 1 The HashTable has the key + * @retval 0 The HashTable does not have the key + */ +extern int +waitui_hashtable_isStolenCheck(waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckFn, + void *arg); + +/** + * @brief Insert a value for the key into the HashTable + * @param[in,out] this The HashTable to insert the value + * @param[in] key The key to insert the value for + * @param[in] value The value to insert + * @retval 1 Ok + * @retval 0 Memory allocation failed or key already exists + */ +extern int waitui_hashtable_insert(waitui_hashtable *this, str key, + void *value); + +/** + * @brief Search for the key inside the HashTable + * @param[in] this The HashTable to lookup the key + * @param[in] key The key to search for in the HashTable + * @return The pointer to the value or NULL if not found + */ +extern void *waitui_hashtable_lookup(waitui_hashtable *this, str key); + +/** + * @brief Test whether the HashTable has the key + * @param[in] this The HashTable to check for the key + * @param[in] key The key to look for in the HashTable + * @retval 1 The HashTable has the key + * @retval 0 The HashTable does not have the key + */ +extern int waitui_hashtable_has(waitui_hashtable *this, str key); + +/** + * @brief Mark value for key as stolen in the HashTable + * @param[in] this The HashTable to check for the key + * @param[in] key The key to look for in the HashTable + * @retval 1 The HashTable has the key + * @retval 0 The HashTable does not have the key + */ +extern int waitui_hashtable_markStolen(waitui_hashtable *this, str key); + +/** + * @brief Tests whether the value for key is marked as stolen in the HashTable + * @param[in] this The HashTable to check for the key + * @param[in] key The key to look for in the HashTable + * @retval 1 The HashTable has the key + * @retval 0 The HashTable does not have the key + */ +extern int waitui_hashtable_isStolen(waitui_hashtable *this, str key); + +#endif//WAITUI_HASHTABLE_H diff --git a/library/hashtable/include/waitui/hashtable_generic.h b/library/hashtable/include/waitui/hashtable_generic.h new file mode 100644 index 0000000..39fbaf9 --- /dev/null +++ b/library/hashtable/include/waitui/hashtable_generic.h @@ -0,0 +1,118 @@ +/** + * @file hashtable_generic.h + * @author rick + * @date 26.07.22 + * @brief File for the generic HashTable implementation + */ + +#ifndef WAITUI_HASHTABLE_GENERIC_H +#define WAITUI_HASHTABLE_GENERIC_H + + +// ----------------------------------------------------------------------------- +// Public defines +// ----------------------------------------------------------------------------- + +#define INTERFACE_HASHTABLE_TYPEDEF(type) \ + typedef waitui_hashtable type##_hashtable + +#define INTERFACE_HASHTABLE_VALUE_CHECK_FN_TYPEDEF(type) \ + typedef waitui_hashtable_value_check_fn type##_hashtable_value_check_fn + +#define INTERFACE_HASHTABLE_NEW(type) \ + extern type##_hashtable *type##_hashtable_new(unsigned long int length) + +#define INTERFACE_HASHTABLE_NEW_CUSTOM(type, elem_destroy) \ + extern type##_hashtable *type##_hashtable_new(unsigned long int length) + +#define INTERFACE_HASHTABLE_DESTROY(type) \ + extern void type##_hashtable_destroy(type##_hashtable **this) + +#define INTERFACE_HASHTABLE_INSERT_CHECK(type) \ + extern int type##_hashtable_insertCheck( \ + type##_hashtable *this, str key, type *type##Element, \ + type##_hashtable_value_check_fn valueCheckFn, void *arg) + +#define INTERFACE_HASHTABLE_LOOKUP_CHECK(type) \ + extern type *type##_hashtable_lookupCheck( \ + type##_hashtable *this, str key, \ + type##_hashtable_value_check_fn valueCheckFn, void *arg) + +#define INTERFACE_HASHTABLE_HAS_CHECK(type) \ + extern int type##_hashtable_hasCheck( \ + type##_hashtable *this, str key, \ + type##_hashtable_value_check_fn valueCheckFn, void *arg) + +#define INTERFACE_HASHTABLE_MARK_STOLEN_CHECK(type) \ + extern int type##_hashtable_markStolenCheck( \ + type##_hashtable *this, str key, \ + type##_hashtable_value_check_fn valueCheckFn, void *arg) + +#define INTERFACE_HASHTABLE_IS_STOLEN_CHECK(type) \ + extern int type##_hashtable_isStolenCheck( \ + type##_hashtable *this, str key, \ + type##_hashtable_value_check_fn valueCheckFn, void *arg) + +#define INTERFACE_HASHTABLE_INSERT(type) \ + extern int type##_hashtable_insert(type##_hashtable *this, str key, \ + type *type##Element) + +#define INTERFACE_HASHTABLE_LOOKUP(type) \ + extern type *type##_hashtable_lookup(type##_hashtable *this, str key) + +#define INTERFACE_HASHTABLE_HAS(type) \ + extern int type##_hashtable_has(type##_hashtable *this, str key) + +#define INTERFACE_HASHTABLE_MARK_STOLEN(type) \ + extern int type##_hashtable_markStolen(type##_hashtable *this, str key) + +#define INTERFACE_HASHTABLE_IS_STOLEN(type) \ + extern int type##_hashtable_isStolen(type##_hashtable *this, str key) + + +/** + * @brief Define for quickly created hashtable implementations for a value type + * @param[in] kind Whether to create interface hashtable definition + * or actual implementation + * @param[in] type For what type to create the hashtable + */ +#define CREATE_HASHTABLE_TYPE(kind, type) \ + kind##_HASHTABLE_TYPEDEF(type); \ + kind##_HASHTABLE_VALUE_CHECK_FN_TYPEDEF(type); \ + kind##_HASHTABLE_NEW(type); \ + kind##_HASHTABLE_DESTROY(type); \ + kind##_HASHTABLE_INSERT_CHECK(type); \ + kind##_HASHTABLE_INSERT(type); \ + kind##_HASHTABLE_LOOKUP_CHECK(type); \ + kind##_HASHTABLE_LOOKUP(type); \ + kind##_HASHTABLE_HAS_CHECK(type); \ + kind##_HASHTABLE_HAS(type); \ + kind##_HASHTABLE_MARK_STOLEN_CHECK(type); \ + kind##_HASHTABLE_MARK_STOLEN(type); \ + kind##_HASHTABLE_IS_STOLEN_CHECK(type); \ + kind##_HASHTABLE_IS_STOLEN(type); + +/** + * @brief Define for quickly created hashtable implementations for a value type + * @param[in] kind Whether to create interface hashtable definition + * or actual implementation + * @param[in] type For what type to create the hashtable + * @param[in] elem_destroy Custom element destroy function + */ +#define CREATE_HASHTABLE_TYPE_CUSTOM(kind, type, elem_destroy) \ + kind##_HASHTABLE_TYPEDEF(type); \ + kind##_HASHTABLE_VALUE_CHECK_FN_TYPEDEF(type); \ + kind##_HASHTABLE_NEW_CUSTOM(type, elem_destroy); \ + kind##_HASHTABLE_DESTROY(type); \ + kind##_HASHTABLE_INSERT_CHECK(type); \ + kind##_HASHTABLE_INSERT(type); \ + kind##_HASHTABLE_LOOKUP_CHECK(type); \ + kind##_HASHTABLE_LOOKUP(type); \ + kind##_HASHTABLE_HAS_CHECK(type); \ + kind##_HASHTABLE_HAS(type); \ + kind##_HASHTABLE_MARK_STOLEN_CHECK(type); \ + kind##_HASHTABLE_MARK_STOLEN(type); \ + kind##_HASHTABLE_IS_STOLEN_CHECK(type); \ + kind##_HASHTABLE_IS_STOLEN(type); + +#endif//WAITUI_HASHTABLE_GENERIC_H diff --git a/library/hashtable/include/waitui/hashtable_generic_impl.h b/library/hashtable/include/waitui/hashtable_generic_impl.h new file mode 100644 index 0000000..a7d3753 --- /dev/null +++ b/library/hashtable/include/waitui/hashtable_generic_impl.h @@ -0,0 +1,113 @@ +/** +* @file hashtable_generic_impl.h +* @author rick +* @date 26.07.22 +* @brief File for the generic HashTable implementation +*/ + +#ifndef WAITUI_HASHTABLE_GENERIC_IMPL_H +#define WAITUI_HASHTABLE_GENERIC_IMPL_H + +#include "waitui/hashtable.h" +#include "waitui/hashtable_generic.h" + + +// ----------------------------------------------------------------------------- +// Public defines +// ----------------------------------------------------------------------------- + +#define IMPLEMENTATION_HASHTABLE_TYPEDEF(type) + +#define IMPLEMENTATION_HASHTABLE_VALUE_CHECK_FN_TYPEDEF(type) + +#define IMPLEMENTATION_HASHTABLE_NEW(type) \ + type##_hashtable *type##_hashtable_new(unsigned long int length) { \ + return (type##_hashtable *) waitui_hashtable_new( \ + length, (waitui_hashtable_value_destroy_fn) type##_destroy); \ + } + +#define IMPLEMENTATION_HASHTABLE_NEW_CUSTOM(type, elem_destroy) \ + type##_hashtable *type##_hashtable_new(unsigned long int length) { \ + return (type##_hashtable *) waitui_hashtable_new( \ + length, (waitui_hashtable_value_destroy_fn) (elem_destroy)); \ + } + +#define IMPLEMENTATION_HASHTABLE_DESTROY(type) \ + void type##_hashtable_destroy(type##_hashtable **this) { \ + waitui_hashtable_destroy((waitui_hashtable **) this); \ + } + +#define IMPLEMENTATION_HASHTABLE_INSERT_CHECK(type) \ + int type##_hashtable_insertCheck( \ + type##_hashtable *this, str key, type *type##Element, \ + waitui_hashtable_value_check_fn valueCheckFn, void *arg) { \ + return waitui_hashtable_insertCheck( \ + (waitui_hashtable *) this, key, (void *) type##Element, \ + (waitui_hashtable_value_check_fn) valueCheckFn, arg); \ + } + +#define IMPLEMENTATION_HASHTABLE_LOOKUP_CHECK(type) \ + type *type##_hashtable_lookupCheck( \ + type##_hashtable *this, str key, \ + waitui_hashtable_value_check_fn valueCheckFn, void *arg) { \ + return (type *) waitui_hashtable_lookupCheck( \ + (waitui_hashtable *) this, key, \ + (waitui_hashtable_value_check_fn) valueCheckFn, arg); \ + } + +#define IMPLEMENTATION_HASHTABLE_HAS_CHECK(type) \ + int type##_hashtable_hasCheck( \ + type##_hashtable *this, str key, \ + waitui_hashtable_value_check_fn valueCheckFn, void *arg) { \ + return waitui_hashtable_hasCheck( \ + (waitui_hashtable *) this, key, \ + (waitui_hashtable_value_check_fn) valueCheckFn, arg); \ + } + +#define IMPLEMENTATION_HASHTABLE_MARK_STOLEN_CHECK(type) \ + int type##_hashtable_markStolenCheck( \ + type##_hashtable *this, str key, \ + waitui_hashtable_value_check_fn valueCheckFn, void *arg) { \ + return waitui_hashtable_markStolenCheck( \ + (waitui_hashtable *) this, key, \ + (waitui_hashtable_value_check_fn) valueCheckFn, arg); \ + } + +#define IMPLEMENTATION_HASHTABLE_IS_STOLEN_CHECK(type) \ + int type##_hashtable_isStolenCheck( \ + type##_hashtable *this, str key, \ + waitui_hashtable_value_check_fn valueCheckFn, void *arg) { \ + return waitui_hashtable_isStolenCheck( \ + (waitui_hashtable *) this, key, \ + (waitui_hashtable_value_check_fn) valueCheckFn, arg); \ + } + +#define IMPLEMENTATION_HASHTABLE_INSERT(type) \ + int type##_hashtable_insert(type##_hashtable *this, str key, \ + type *type##Element) { \ + return waitui_hashtable_insert((waitui_hashtable *) this, key, \ + (void *) type##Element); \ + } + +#define IMPLEMENTATION_HASHTABLE_LOOKUP(type) \ + type *type##_hashtable_lookup(type##_hashtable *this, str key) { \ + return (type *) waitui_hashtable_lookup((waitui_hashtable *) this, \ + key); \ + } + +#define IMPLEMENTATION_HASHTABLE_HAS(type) \ + int type##_hashtable_has(type##_hashtable *this, str key) { \ + return waitui_hashtable_has((waitui_hashtable *) this, key); \ + } + +#define IMPLEMENTATION_HASHTABLE_MARK_STOLEN(type) \ + int type##_hashtable_markStolen(type##_hashtable *this, str key) { \ + return waitui_hashtable_markStolen((waitui_hashtable *) this, key); \ + } + +#define IMPLEMENTATION_HASHTABLE_IS_STOLEN(type) \ + int type##_hashtable_isStolen(type##_hashtable *this, str key) { \ + return waitui_hashtable_isStolen((waitui_hashtable *) this, key); \ + } + +#endif//WAITUI_HASHTABLE_GENERIC_IMPL_H diff --git a/library/hashtable/project-meta-info.in b/library/hashtable/project-meta-info.in new file mode 100644 index 0000000..0803f8d --- /dev/null +++ b/library/hashtable/project-meta-info.in @@ -0,0 +1,3 @@ +set(project_version 0.0.1) +set(project_description "waitui hashtable library") +set(project_homepage "http://example.com") \ No newline at end of file diff --git a/library/hashtable/src/hashtable.c b/library/hashtable/src/hashtable.c new file mode 100644 index 0000000..36d7613 --- /dev/null +++ b/library/hashtable/src/hashtable.c @@ -0,0 +1,215 @@ +/** + * @file hashtable.c + * @author rick + * @date 23.07.20 + * @brief File for the HashTable implementation + */ + +#include "waitui/hashtable.h" + +#include +#include + + +// ----------------------------------------------------------------------------- +// Local functions +// ----------------------------------------------------------------------------- + +/** + * @brief Calculate the HashTable slot for the given key + * @param[in] this The HashTable to calculate the slot for + * @param[in] key The key to calculate the slot for + * @return The slot in the HashTable for the given key + */ +static inline unsigned long int waitui_hashtable_hash(waitui_hashtable *this, + str key) { + unsigned long int hashValue = 0; + + if (key.len < 1 || !key.s) { return 0; } + + for (unsigned long int i = 0; i < key.len; ++i) { hashValue += key.s[i]; } + hashValue += key.s[0] % 11 + (((unsigned char) key.s[0]) << 3U) - key.s[0]; + + return hashValue % this->size; +} + + +// ----------------------------------------------------------------------------- +// Public functions +// ----------------------------------------------------------------------------- + +waitui_hashtable * +waitui_hashtable_new(unsigned long int size, + waitui_hashtable_value_destroy_fn valueDestroyFn) { + waitui_hashtable *this = NULL; + + this = calloc(1, sizeof(*this)); + if (!this) { return NULL; } + + this->size = size; + this->valueDestroyFn = valueDestroyFn; + this->list = calloc(size, sizeof(this->list)); + if (!this->list) { + free(this); + return NULL; + } + + for (unsigned long int i = 0; i < this->size; ++i) { this->list[i] = NULL; } + + return this; +} + +void waitui_hashtable_destroy(waitui_hashtable **this) { + if (!this || !(*this)) { return; } + + if ((*this)->list) { + for (unsigned long int i = 0; i < (*this)->size; ++i) { + while ((*this)->list[i]) { + waitui_hashtable_node *temp = (*this)->list[i]; + (*this)->list[i] = (*this)->list[i]->next; + + if (!temp->isStolen) { (*this)->valueDestroyFn(&temp->value); } + + STR_FREE(&temp->key); + + free(temp); + } + } + free((*this)->list); + } + + free(*this); + *this = NULL; +} + +int waitui_hashtable_insertCheck(waitui_hashtable *this, str key, void *value, + waitui_hashtable_value_check_fn valueCheckFn, + void *arg) { + if (!this || !key.s) { return 0; } + + unsigned long int hashSlot = waitui_hashtable_hash(this, key); + waitui_hashtable_node *node = this->list[hashSlot]; + str keyCopy = STR_NULL_INIT; + + while (node && (key.len != node->key.len || + memcmp(key.s, node->key.s, key.len) != 0 || + (valueCheckFn && !valueCheckFn(node->value, arg)))) { + node = node->next; + } + + if (node) { return 0; } + + node = calloc(1, sizeof(*node)); + if (!node) { return 0; } + + STR_COPY(&keyCopy, &key); + if (!keyCopy.s) { + free(node); + return 0; + } + + node->key = keyCopy; + node->value = value; + node->next = this->list[hashSlot]; + this->list[hashSlot] = node; + + return 1; +} + +void * +waitui_hashtable_lookupCheck(waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckFn, + void *arg) { + if (!this || !key.s) { return NULL; } + + unsigned long int hashValue = waitui_hashtable_hash(this, key); + waitui_hashtable_node *node = this->list[hashValue]; + + while (node && (key.len != node->key.len || + memcmp(key.s, node->key.s, key.len) != 0 || + (valueCheckFn && !valueCheckFn(node->value, arg)))) { + node = node->next; + } + + if (!node) { return NULL; } + + return node->value; +} + +int waitui_hashtable_hasCheck( + waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckCallback, void *arg) { + if (!this || !key.s) { return 0; } + + unsigned long int hashValue = waitui_hashtable_hash(this, key); + waitui_hashtable_node *node = this->list[hashValue]; + + while (node && + (key.len != node->key.len || + memcmp(key.s, node->key.s, key.len) != 0 || + (valueCheckCallback && !valueCheckCallback(node->value, arg)))) { + node = node->next; + } + + return node != NULL; +} + +int waitui_hashtable_markStolenCheck( + waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckFn, void *arg) { + if (!this || !key.s) { return 0; } + + unsigned long int hashValue = waitui_hashtable_hash(this, key); + waitui_hashtable_node *node = this->list[hashValue]; + + while (node && (key.len != node->key.len || + memcmp(key.s, node->key.s, key.len) != 0 || + (valueCheckFn && !valueCheckFn(node->value, arg)))) { + node = node->next; + } + + if (!node) { return 0; } + + node->isStolen = 1; + + return 1; +} + +int waitui_hashtable_isStolenCheck( + waitui_hashtable *this, str key, + waitui_hashtable_value_check_fn valueCheckFn, void *arg) { + if (!this || !key.s) { return 0; } + + unsigned long int hashValue = waitui_hashtable_hash(this, key); + waitui_hashtable_node *node = this->list[hashValue]; + + while (node && (key.len != node->key.len || + memcmp(key.s, node->key.s, key.len) != 0 || + (valueCheckFn && !valueCheckFn(node->value, arg)))) { + node = node->next; + } + + if (!node) { return 0; } + + return node->isStolen; +} + +int waitui_hashtable_insert(waitui_hashtable *this, str key, void *value) { + return waitui_hashtable_insertCheck(this, key, value, NULL, NULL); +} + +void *waitui_hashtable_lookup(waitui_hashtable *this, str key) { + return waitui_hashtable_lookupCheck(this, key, NULL, NULL); +} + +int waitui_hashtable_has(waitui_hashtable *this, str key) { + return waitui_hashtable_hasCheck(this, key, NULL, NULL); +} + +int waitui_hashtable_markStolen(waitui_hashtable *this, str key) { + return waitui_hashtable_markStolenCheck(this, key, NULL, NULL); +} + +int waitui_hashtable_isStolen(waitui_hashtable *this, str key) { + return waitui_hashtable_isStolenCheck(this, key, NULL, NULL); +} diff --git a/library/list/CMakeLists.txt b/library/list/CMakeLists.txt index 6d1a26f..8e83b39 100644 --- a/library/list/CMakeLists.txt +++ b/library/list/CMakeLists.txt @@ -19,4 +19,4 @@ target_sources(list "include/waitui/list_generic_impl.h" ) -target_include_directories(list PUBLIC "include") \ No newline at end of file +target_include_directories(list PUBLIC "include") diff --git a/library/list/include/waitui/list.h b/library/list/include/waitui/list.h index ad4cf0e..006417b 100644 --- a/library/list/include/waitui/list.h +++ b/library/list/include/waitui/list.h @@ -127,4 +127,4 @@ extern void *waitui_list_iter_next(waitui_list_iter *this); */ extern void waitui_list_iter_destroy(waitui_list_iter **this); -#endif//WAITUI_LIST_H \ No newline at end of file +#endif//WAITUI_LIST_H diff --git a/library/list/include/waitui/list_generic.h b/library/list/include/waitui/list_generic.h index 4831768..8a91573 100644 --- a/library/list/include/waitui/list_generic.h +++ b/library/list/include/waitui/list_generic.h @@ -75,4 +75,4 @@ kind##_LIST_ITER_NEXT(type); \ kind##_LIST_ITER_DESTROY(type); -#endif//WAITUI_LIST_GENERIC_H \ No newline at end of file +#endif//WAITUI_LIST_GENERIC_H diff --git a/library/list/include/waitui/list_generic_impl.h b/library/list/include/waitui/list_generic_impl.h index f937726..5bc6867 100644 --- a/library/list/include/waitui/list_generic_impl.h +++ b/library/list/include/waitui/list_generic_impl.h @@ -76,4 +76,4 @@ waitui_list_iter_destroy((waitui_list_iter **) this); \ } -#endif//WAITUI_LIST_GENERIC_IMPL_H \ No newline at end of file +#endif//WAITUI_LIST_GENERIC_IMPL_H diff --git a/library/list/src/list.c b/library/list/src/list.c index 5f2cf71..a21c190 100644 --- a/library/list/src/list.c +++ b/library/list/src/list.c @@ -216,4 +216,4 @@ void waitui_list_iter_destroy(waitui_list_iter **this) { free(*this); *this = NULL; -} \ No newline at end of file +} diff --git a/library/log/include/waitui/log.h b/library/log/include/waitui/log.h index 645c61a..2ffc07e 100644 --- a/library/log/include/waitui/log.h +++ b/library/log/include/waitui/log.h @@ -19,7 +19,7 @@ // ----------------------------------------------------------------------------- /** - * @brief The type for log events. + * @brief The type for log events */ typedef struct waitui_log_event { va_list ap; @@ -32,17 +32,17 @@ typedef struct waitui_log_event { } waitui_log_event; /** - * @brief The logging callback function type. + * @brief The logging callback function type */ typedef void (*waitui_log_logging_fn)(waitui_log_event *event); /** - * @brief The locking callback function type. + * @brief The locking callback function type */ typedef void (*waitui_log_lock_fn)(bool lock, void *userData); /** - * @brief The different log levels. + * @brief The different log levels */ typedef enum { WAITUI_LOG_TRACE, @@ -78,26 +78,26 @@ typedef enum { // ----------------------------------------------------------------------------- /** - * @brief Set locking function for log. + * @brief Set locking function for log * @param lockFn The function to be called for lock and unlock when writing logs * @param[in] userData Extra user data to pass to the locking function */ extern void waitui_log_set_lock(waitui_log_lock_fn lockFn, void *userData); /** - * @brief Set minimum log level to log on stderr. + * @brief Set minimum log level to log on stderr * @param level The minimum log level from which on log appear on stderr */ extern void waitui_log_setLevel(waitui_log_level level); /** - * @brief Disable or enable logging to stderr. + * @brief Disable or enable logging to stderr * @param enable True to disable logging to stderr, false to enable */ extern void waitui_log_setQuiet(bool enable); /** - * @brief Add the logFn as a log callback to write logs starting at the level. + * @brief Add the logFn as a log callback to write logs starting at the level * @param logFn The function to add as a log callback * @param[in] userData Extra user data to pass to the logFn * @param level The level from which on this log callback is executed @@ -108,23 +108,23 @@ extern int waitui_log_addCallback(waitui_log_logging_fn logFn, void *userData, waitui_log_level level); /** -* @brief Add a log callback to write logs starting at the level into the file. -* @param[in] file The file into which to write the log -* @param level The level from which on this log callback is executed -* @retval 1 Successful added the file callback -* @retval 0 No more space to add the callback -*/ + * @brief Add a log callback to write logs starting at the level into the file + * @param[in] file The file into which to write the log + * @param level The level from which on this log callback is executed + * @retval 1 Successful added the file callback + * @retval 0 No more space to add the callback + */ extern int waitui_log_addFile(FILE *file, waitui_log_level level); /** -* @brief Write the log message to log with the given parameters. -* @param level The log level for this message -* @param[in] file The filename where this log is written -* @param line The line number where this log is written -* @param[in] format The message to write to the log -* @param ... Extra values to be used inside the message -*/ + * @brief Write the log message to log with the given parameters + * @param level The log level for this message + * @param[in] file The filename where this log is written + * @param line The line number where this log is written + * @param[in] format The message to write to the log + * @param ... Extra values to be used inside the message + */ extern void waitui_log_writeLog(waitui_log_level level, const char *file, int line, const char *format, ...); -#endif//WAITUI_LOG_H \ No newline at end of file +#endif//WAITUI_LOG_H diff --git a/library/log/src/log.c b/library/log/src/log.c index 7e6fd2f..e4b2b52 100644 --- a/library/log/src/log.c +++ b/library/log/src/log.c @@ -13,7 +13,7 @@ // ----------------------------------------------------------------------------- /** - * @brief The maximum number of possible callbacks to register. + * @brief The maximum number of possible callbacks to register */ #define MAX_CALLBACKS 32 @@ -23,7 +23,7 @@ // ----------------------------------------------------------------------------- /** - * @brief Internal type to store the log callback function with all its info. + * @brief Internal type to store the log callback function with all its info */ typedef struct waitui_log_callback { waitui_log_logging_fn logFn; @@ -32,7 +32,7 @@ typedef struct waitui_log_callback { } waitui_log_callback; /** - * @brief Internal type to store the log with all its info. + * @brief Internal type to store the log with all its info */ typedef struct waitui_log { waitui_log_lock_fn lockFn; @@ -48,12 +48,12 @@ typedef struct waitui_log { // ----------------------------------------------------------------------------- /** - * @brief The internal state of the log lib. + * @brief The internal state of the log lib */ static struct waitui_log L; /** - * @brief String representations of the log levels. + * @brief String representations of the log levels */ static const char *waitui_log_level_strings[] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", @@ -61,7 +61,7 @@ static const char *waitui_log_level_strings[] = { #ifdef LOG_USE_COLOR /** - * @brief Color representations of the log levels. + * @brief Color representations of the log levels */ static const char *waitui_log_level_colors[] = { "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m", @@ -74,7 +74,7 @@ static const char *waitui_log_level_colors[] = { // ----------------------------------------------------------------------------- /** - * @brief Return the string representations of the log level. + * @brief Return the string representations of the log level * @param level The level to return as a string. * @return The string representations of the log level or empty when out of bounds. */ @@ -84,7 +84,7 @@ static inline const char *waitui_log_levelAsString(waitui_log_level level) { } /** - * @brief Return the color representations of the log level. + * @brief Return the color representations of the log level * @param level The level to return as a color. * @return The color representations of the log level or empty when out of bounds. */ @@ -94,21 +94,21 @@ static inline const char *waitui_log_levelAsColor(waitui_log_level level) { } /** - * @brief Calls the locking callback function to get the lock. + * @brief Calls the locking callback function to get the lock */ static inline void waitui_log_lock(void) { if (L.lockFn) { L.lockFn(true, L.userData); } } /** - * @brief Calls the locking callback function to release the lock. + * @brief Calls the locking callback function to release the lock */ static inline void waitui_log_unlock(void) { if (L.lockFn) { L.lockFn(false, L.userData); } } /** - * @brief Callback that write the log event to the console. + * @brief Callback that write the log event to the console * @param[in] event log event to write to the console */ static void waitui_log_console_callback(waitui_log_event *event) { @@ -130,7 +130,7 @@ static void waitui_log_console_callback(waitui_log_event *event) { } /** - * @brief Callback that write the log event to a file. + * @brief Callback that write the log event to a file * @param[in] event log event to write to a file */ static void waitui_log_file_callback(waitui_log_event *event) { @@ -147,7 +147,7 @@ static void waitui_log_file_callback(waitui_log_event *event) { } /** - * @brief Initializes the log event with time and userdata. + * @brief Initializes the log event with time and userdata * @param[in,out] event log event to write to initialize * @param[in] user_data user data to set for the log event */ @@ -221,4 +221,4 @@ void waitui_log_writeLog(waitui_log_level level, const char *file, int line, } waitui_log_unlock(); -} \ No newline at end of file +} diff --git a/library/str/CMakeLists.txt b/library/str/CMakeLists.txt new file mode 100644 index 0000000..0e54641 --- /dev/null +++ b/library/str/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) + +include("project-meta-info.in") + +project(waitui-str + VERSION ${project_version} + DESCRIPTION ${project_description} + HOMEPAGE_URL ${project_homepage} + LANGUAGES C) + +add_library(str OBJECT) + +target_sources(str + PRIVATE + "src/str.c" + PUBLIC + "include/waitui/str.h" + ) + +target_include_directories(str PUBLIC "include") diff --git a/library/str/include/waitui/str.h b/library/str/include/waitui/str.h new file mode 100644 index 0000000..ffafa37 --- /dev/null +++ b/library/str/include/waitui/str.h @@ -0,0 +1,113 @@ +/** +* @file str.h +* @author rick +* @date 19.02.20 +* @brief File for the String implementation +*/ + +#ifndef WAITUI_STR_H +#define WAITUI_STR_H + +#include +#include + + +// ----------------------------------------------------------------------------- +// Public types +// ----------------------------------------------------------------------------- + +/** +* @brief Type for strings that do not need to be '\0' terminated +*/ +typedef struct str { + char *s; + unsigned long int len; +} str; + + +// ----------------------------------------------------------------------------- +// Public defines +// ----------------------------------------------------------------------------- + +#define STR_NULL_INIT \ + { NULL, 0 } + +#define STR_STATIC_INIT(_str_) \ + { (_str_), sizeof((_str_)) - 1 } + +#define STR_FMT(_pstr_) \ + (((_pstr_) != (str *) 0) ? (int) (_pstr_)->len : 0), \ + (((_pstr_) != (str *) 0) ? (_pstr_)->s : "") + +#define STR_STATIC_SET(_pstr_, _str_) \ + do { \ + if ((_pstr_)) { \ + (_pstr_)->s = (_str_); \ + (_pstr_)->len = sizeof((_str_)) - 1; \ + } \ + } while (0) + +#define STR_STATIC_COPY(_dstr_, _str_) \ + do { \ + if ((_dstr_)) { \ + char *tmp = (_str_); \ + (_dstr_)->len = sizeof((_str_)) - 1; \ + (_dstr_)->s = calloc((_dstr_)->len, sizeof(*(_dstr_)->s)); \ + if ((_dstr_)->s) { \ + memcpy((_dstr_)->s, tmp, (_dstr_)->len); \ + } else { \ + (_dstr_)->len = 0; \ + } \ + } \ + } while (0) + +#define STR_COPY(_dstr_, _pstr_) \ + do { \ + if ((_pstr_) && (_dstr_)) { \ + (_dstr_)->len = (_pstr_)->len; \ + (_dstr_)->s = calloc((_pstr_)->len, sizeof(*(_pstr_)->s)); \ + if ((_dstr_)->s) { \ + memcpy((_dstr_)->s, (_pstr_)->s, (_dstr_)->len); \ + } else { \ + (_dstr_)->len = 0; \ + } \ + } \ + } while (0) + +#define STR_COPY_WITH_NUL(_dstr_, _pstr_) \ + do { \ + if ((_pstr_) && (_dstr_)) { \ + (_dstr_)->len = (_pstr_)->len; \ + (_dstr_)->s = calloc((_pstr_)->len + 1, sizeof(*(_pstr_)->s)); \ + if ((_dstr_)->s) { \ + memcpy((_dstr_)->s, (_pstr_)->s, (_dstr_)->len); \ + } else { \ + (_dstr_)->len = 0; \ + } \ + } \ + } while (0) + +#define STR_SNPRINTF(_dstr_, _fmt_, ...) \ + do { \ + if ((_dstr_)) { \ + (_dstr_)->len = snprintf(NULL, 0, (_fmt_), __VA_ARGS__); \ + (_dstr_)->s = calloc((_dstr_)->len + 1, sizeof(*(_dstr_)->s)); \ + if ((_dstr_)->s) { \ + snprintf((_dstr_)->s, (_dstr_)->len + 1, (_fmt_), \ + __VA_ARGS__); \ + } else { \ + (_dstr_)->len = 0; \ + } \ + } \ + } while (0) + +#define STR_FREE(_pstr_) \ + do { \ + if ((_pstr_)) { \ + if ((_pstr_)->s) { free((_pstr_)->s); }; \ + (_pstr_)->len = 0; \ + (_pstr_)->s = NULL; \ + } \ + } while (0) + +#endif//WAITUI_STR_H diff --git a/library/str/project-meta-info.in b/library/str/project-meta-info.in new file mode 100644 index 0000000..90a4459 --- /dev/null +++ b/library/str/project-meta-info.in @@ -0,0 +1,3 @@ +set(project_version 0.0.1) +set(project_description "waitui str library") +set(project_homepage "http://example.com") \ No newline at end of file diff --git a/library/str/src/str.c b/library/str/src/str.c new file mode 100644 index 0000000..4e9cf2f --- /dev/null +++ b/library/str/src/str.c @@ -0,0 +1,8 @@ +/** +* @file str.c +* @author rick +* @date 19.02.20 +* @brief File for the String implementation +*/ + +#include "waitui/str.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0a807bf..6203bb3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,5 @@ project(waitui-tests HOMEPAGE_URL ${project_homepage} LANGUAGES C) -#option(ENABLE_TEST_COVERAGE "Enable test coverage" ON) - -add_subdirectory(library/list) \ No newline at end of file +add_subdirectory(library/hashtable) +add_subdirectory(library/list) diff --git a/tests/library/hashtable/CMakeLists.txt b/tests/library/hashtable/CMakeLists.txt new file mode 100644 index 0000000..3fe8f97 --- /dev/null +++ b/tests/library/hashtable/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(waitui-test_hashtable) + +target_sources(waitui-test_hashtable + PRIVATE + "test_hashtable.c" + ) + +target_link_libraries(waitui-test_hashtable PRIVATE hashtable) + +add_test(NAME waitui-test_hashtable COMMAND waitui-test_hashtable) + +if(ENABLE_TEST_COVERAGE) + target_compile_options(hashtable PUBLIC -O0 -g -fprofile-arcs -ftest-coverage) + target_link_options(hashtable PUBLIC -fprofile-arcs -ftest-coverage) +endif() diff --git a/tests/library/hashtable/test_hashtable.c b/tests/library/hashtable/test_hashtable.c new file mode 100644 index 0000000..7479cc0 --- /dev/null +++ b/tests/library/hashtable/test_hashtable.c @@ -0,0 +1,321 @@ +/** +* @file test_hashtable.c +* @author rick +* @date 27.07.22 +* @brief Test for the HashTable implementation +*/ + +#include "waitui/hashtable_generic.h" +#include "waitui/hashtable_generic_impl.h" + +#include "../../bdd-for-c-ext.h" + +typedef struct value { + int count; + int i; +} value; + +static value *value_new(int i) { + value *this = calloc(1, sizeof(*this)); + if (!this) { return NULL; } + this->i = i; + return this; +} + +static void value_destroy(value **this) { + if (!this || !(*this)) { return; } + free(*this); + *this = NULL; +} + +static int value_check_counter(value *this, int *count) { + if (!this) { return 0; } + return this->count == *count; +} + +CREATE_HASHTABLE_TYPE(INTERFACE, value) +CREATE_HASHTABLE_TYPE(IMPLEMENTATION, value) + +spec("hashtable") { + context("use as a normal hashtable with no forced collisions") { + static value_hashtable *hashtable = NULL; + + before_each() { + hashtable = value_hashtable_new(42); + check(hashtable != NULL, "hashtable should not be NULL"); + } + + after_each() { + value_hashtable_destroy(&hashtable); + check(hashtable == NULL, "hashtable should be NULL"); + } + + describe("value_hashtable_insert()") { + for_it( + "should add values to the end of the list and it should be there", + params_format(2, "start: %d", "elements: %d"), + format_args(test_param.start, test_param.count), + { + int start; + int count; + }, + test_params( + {0, 0}, + {0, 1}, + {1, 1}, + {4, 20}, + ) + ) { + for (int i = test_param.start; i < test_param.start + test_param.count; ++i) { + str key = STR_NULL_INIT; + value *value = NULL; + + STR_SNPRINTF(&key, "key-%d", i); + check(key.s != NULL, "key.s should be not NULL"); + + check(value_hashtable_has(hashtable, key) == 0, "hashtable should not have the key already"); + check(value_hashtable_insert(hashtable, key, value_new(i)) == 1); + check(value_hashtable_has(hashtable, key) == 1, "hashtable should have the key"); + value = value_hashtable_lookup(hashtable, key); + check(value != NULL, "value should not be NULL"); + check(value->i == i, "value should be %d but is %d", i, value->i); + + STR_FREE(&key); + } + + for (int i = test_param.start + test_param.count - 1; i >= test_param.start; --i) { + str key = STR_NULL_INIT; + value *value = NULL; + + STR_SNPRINTF(&key, "key-%d", i); + check(key.s != NULL, "key.s should be not NULL"); + + value = value_hashtable_lookup(hashtable, key); + check(value != NULL, "value should not be NULL"); + check(value->i == i, "value should be %d but is %d", i, value->i); + + STR_FREE(&key); + } + } + for_it_end(); + + it("should work with the empty str") { + str key = STR_STATIC_INIT(""); + + check(value_hashtable_has(hashtable, key) == 0, "hashtable should not have the key already"); + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 1); + check(value_hashtable_has(hashtable, key) == 1, "hashtable should have the key"); + } + + it("should not work with the str having NULL as string") { + str key = STR_NULL_INIT; + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 0); + } + + it("should return 0 with NULL as this param") { + str key = STR_STATIC_INIT("leet"); + check(value_hashtable_insert(NULL, key, value_new(1)) == 0, "return should be 0"); + } + + it("should not allow double entry") { + str key = STR_STATIC_INIT("42"); + + check(value_hashtable_has(hashtable, key) == 0, "hashtable should not have the key already"); + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 1); + check(value_hashtable_has(hashtable, key) == 1, "hashtable should have the key"); + + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 0); + } + } + + describe("value_hashtable_insertCheck()") { + it("should allow a double insert when check test the value and returns true") { + int counter; + str key = STR_STATIC_INIT("foo"); + + value *value1 = value_new(1337); + value1->count = 0; + + value *value2 = value_new(7331); + value2->count = 1; + + value *resultValue = NULL; + + counter = 0; + + check(value_hashtable_insertCheck(hashtable, key, value1, (value_hashtable_value_check_fn) value_check_counter, &counter) == 1); + check(value_hashtable_hasCheck(hashtable, key, (value_hashtable_value_check_fn) value_check_counter, &counter) == 1); + check(value_hashtable_insertCheck(hashtable, key, value1, (value_hashtable_value_check_fn) value_check_counter, &counter) == 0); + + counter = 1; + + check(value_hashtable_insertCheck(hashtable, key, value2, (value_hashtable_value_check_fn) value_check_counter, &counter) == 1); + check(value_hashtable_hasCheck(hashtable, key, (value_hashtable_value_check_fn) value_check_counter, &counter) == 1); + check(value_hashtable_insertCheck(hashtable, key, value2, (value_hashtable_value_check_fn) value_check_counter, &counter) == 0); + + counter = 0; + resultValue = value_hashtable_lookupCheck(hashtable, key, (value_hashtable_value_check_fn) value_check_counter, &counter); + check(resultValue != NULL, "resultValue should not be NULL"); + check(resultValue->i == 1337, "resultValue->i should be %d", 1337); + check(resultValue->count == 0, "resultValue->count should be %d", 0); + + counter = 1; + resultValue = value_hashtable_lookupCheck(hashtable, key, (value_hashtable_value_check_fn) value_check_counter, &counter); + check(resultValue != NULL, "resultValue should not be NULL"); + check(resultValue->i == 7331, "resultValue->i should be %d", 7331); + check(resultValue->count == 1, "resultValue->count should be %d", 1); + check(value_hashtable_markStolenCheck(hashtable, key, (value_hashtable_value_check_fn) value_check_counter, &counter) == 1); + check(value_hashtable_isStolenCheck(hashtable, key, (value_hashtable_value_check_fn) value_check_counter, &counter) == 1); + value_destroy(&resultValue); + check(resultValue == NULL, "resultValue should be NULL"); + } + } + + describe("value_hashtable_destroy()") { + it("should work with NULL as this param") { + value_hashtable_destroy(NULL); + } + + it("should work with a pointer to NULL as this param") { + static value_hashtable *empty = NULL; + value_hashtable_destroy(&empty); + check(empty == NULL, "empty should be NULL"); + } + } + + describe("value_hashtable_lookup()") { + it("should work with the empty str") { + str key = STR_STATIC_INIT(""); + value *value = value_hashtable_lookup(hashtable, key); + check(value == NULL, "value should be NULL"); + } + + it("should not work with the str having NULL as string") { + str key = STR_NULL_INIT; + value *value = value_hashtable_lookup(hashtable, key); + check(value == NULL, "value should be NULL"); + } + + it("should return NULL with NULL as this param") { + str key = STR_STATIC_INIT("leet"); + value *value = value_hashtable_lookup(NULL, key); + check(value == NULL, "value should be NULL"); + } + } + + describe("value_hashtable_markStolen()") { + it("should mark the value for the given key as stolen") { + str key = STR_STATIC_INIT("leet"); + + check(value_hashtable_has(hashtable, key) == 0,"hashtable should not have the key already"); + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 1); + check(value_hashtable_has(hashtable, key) == 1, "hashtable should have the key"); + check(value_hashtable_markStolen(hashtable, key) == 1, "hashtable should mark the value for the key as stolen"); + check(value_hashtable_isStolen(hashtable, key) == 1, "hashtable should have the value for the key marked as stolen"); + } + + it("should return 0 the given key that is not existing") { + str keyExists = STR_STATIC_INIT("other"); + check(value_hashtable_insert(hashtable, keyExists, value_new(1337)) == 1); + str key = STR_STATIC_INIT("leet"); + check(value_hashtable_has(hashtable, key) == 0,"hashtable should not have the key already"); + check(value_hashtable_markStolen(hashtable, key) == 0, "hashtable should mark the value for the key as stolen"); + } + + it("should work with the empty str") { + str key = STR_STATIC_INIT(""); + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 1); + check(value_hashtable_markStolen(hashtable, key) == 1, "return should be 1"); + } + + it("should not work with the str having NULL as string") { + str key = STR_NULL_INIT; + check(value_hashtable_markStolen(hashtable, key) == 0, "return should be 0"); + } + + it("should return 0 with NULL as this param") { + str key = STR_STATIC_INIT("leet"); + check(value_hashtable_markStolen(NULL, key) == 0, "return should be 0"); + } + } + + describe("value_hashtable_has()") { + it("should work with the empty str") { + str key = STR_STATIC_INIT(""); + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 1); + check(value_hashtable_has(hashtable, key) == 1, "return should be 1"); + } + + it("should not work with the str having NULL as string") { + str key = STR_NULL_INIT; + check(value_hashtable_has(hashtable, key) == 0, "return should be 0"); + } + + it("should return NULL with NULL as this param") { + str key = STR_STATIC_INIT("leet"); + check(value_hashtable_has(NULL, key) == 0, "return should be 0"); + } + } + + describe("value_hashtable_isStolen()") { + it("should work with the empty str") { + str key = STR_STATIC_INIT(""); + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 1); + check(value_hashtable_isStolen(hashtable, key) == 0, "return should be 0"); + } + + it("should not work with the str having NULL as string") { + str key = STR_NULL_INIT; + check(value_hashtable_isStolen(hashtable, key) == 0, "return should be 0"); + } + + it("should return NULL with NULL as this param") { + str key = STR_STATIC_INIT("leet"); + check(value_hashtable_isStolen(NULL, key) == 0, "return should be 0"); + } + + it("should return 0 with a not stolen value") { + str key = STR_STATIC_INIT("test"); + str otherKey1 = STR_STATIC_INIT("otherKey1"); + check(value_hashtable_insert(hashtable, otherKey1, value_new(1337)) == 1); + check(value_hashtable_isStolen(hashtable, key) == 0, "return should be 0"); + } + } + } + + context("use as a normal hashtable with forced collisions") { + static value_hashtable *hashtable = NULL; + + before_each() { + hashtable = value_hashtable_new(2); + check(hashtable != NULL, "hashtable should not be NULL"); + } + + after_each() { + value_hashtable_destroy(&hashtable); + check(hashtable == NULL, "hashtable should be NULL"); + } + + describe("value_hashtable_markStolen()") { + it("should mark the value for the given key as stolen") { + str key = STR_STATIC_INIT("leet"); + str otherKey1 = STR_STATIC_INIT("otherKey1"); + str otherKey2 = STR_STATIC_INIT("otherKey2"); + str otherKey3 = STR_STATIC_INIT("otherKey3"); + str otherKey4 = STR_STATIC_INIT("otherKey4"); + + check(value_hashtable_has(hashtable, key) == 0,"hashtable should not have the key already"); + check(value_hashtable_insert(hashtable, key, value_new(1337)) == 1); + check(value_hashtable_has(hashtable, key) == 1, "hashtable should have the key"); + + check(value_hashtable_insert(hashtable, otherKey1, value_new(1337)) == 1); + check(value_hashtable_insert(hashtable, otherKey2, value_new(1337)) == 1); + check(value_hashtable_insert(hashtable, otherKey3, value_new(1337)) == 1); + check(value_hashtable_insert(hashtable, otherKey4, value_new(1337)) == 1); + + check(value_hashtable_markStolen(hashtable, key) == 1, "hashtable should mark the value for the key as stolen"); + check(value_hashtable_isStolen(hashtable, key) == 1, "hashtable should have the value for the key marked as stolen"); + } + } + } +} diff --git a/tests/library/list/test_list.c b/tests/library/list/test_list.c index 40e8206..faa9aea 100644 --- a/tests/library/list/test_list.c +++ b/tests/library/list/test_list.c @@ -64,6 +64,7 @@ spec("list") { for (int i = test_param.start; i < test_param.start + test_param.count; ++i) { check(value_list_push(list, value_new(i)) == 1); value *value = value_list_peek(list); + check(value != NULL, "value should not be NULL"); check(value->i == i, "value should be %d but is %d", i, value->i); } @@ -230,7 +231,6 @@ spec("list") { it("should work with a pointer to NULL as this param") { static value_list *empty = NULL; - value_list_destroy(NULL); value_list_destroy(&empty); check(empty == NULL, "empty should be NULL"); } @@ -250,4 +250,4 @@ spec("list") { } } } -} \ No newline at end of file +}