From b5f2fa91f37c67d3b23ff2870a0c289bb6e92469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Bori?= Date: Mon, 2 Sep 2024 03:32:03 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20(doxygen):=20Added=20Doxygen=20d?= =?UTF-8?q?ocumentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + CMakeLists.txt | 9 +++- README.md | 9 +++- default.nix | 23 ++++++++-- doc/CMakeLists.txt | 41 +++++++++++++++++ doc/Doxyfile.in | 35 +++++++++++++++ flake.nix | 4 +- src/common/hash.c | 12 ++++- src/common/hash.h | 7 +++ src/common/key.c | 10 +++-- src/common/key.h | 20 +++++++++ src/common/table.h | 4 ++ src/common/types.h | 8 ++++ src/open_addressing/entry.h | 4 ++ src/open_addressing/table.c | 84 +++++++++++++++++++++++++++++------ src/open_addressing/table.h | 49 ++++++++++++++++++++ src/separate_chaining/entry.c | 16 +++++-- src/separate_chaining/entry.h | 14 ++++++ src/separate_chaining/table.c | 69 +++++++++++++++++++++++----- src/separate_chaining/table.h | 49 ++++++++++++++++++++ src/tinyhash.c | 15 ++++++- src/tinyhash.h | 81 +++++++++++++++++++++++++++++++++ tests/test_table.c | 6 --- 23 files changed, 525 insertions(+), 45 deletions(-) create mode 100644 doc/CMakeLists.txt create mode 100644 doc/Doxyfile.in diff --git a/.gitignore b/.gitignore index 976f52b..c828854 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ build .direnv result /debug.c +a.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e5b0e..332a9de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,16 @@ cmake_minimum_required(VERSION 3.10) -project(tinyhash VERSION 0.0.1 LANGUAGES C DESCRIPTION "Simple C hash table implementation.") +project(tinyhash VERSION 0.0.1 LANGUAGES C DESCRIPTION "This is a library containing multiple C implementations of hashmap.") + +option(BUILD_TESTS "Build the tests" OFF) +option(BUILD_DOC "Build the documentation" OFF) add_subdirectory(src) if (BUILD_TESTS) add_subdirectory(tests) endif () + +if (BUILD_DOC) + add_subdirectory(doc) +endif () \ No newline at end of file diff --git a/README.md b/README.md index 7433957..172d66f 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ The following CMake build arguments are available to enable or disable options. | -- | -- | -- | | `-DBUILD_TESTS` | Compile the test files | **`ON`** | `-DBUILD_STATIC` | Link as a static library (instead of a shared library) | **`OFF`** +| `-DBUILD_DOC` | Build the documentation | **`OFF`** ## 🤝 Contribute @@ -48,6 +49,12 @@ The generated Makefile will contain a special `test` target, so you can run the make test ``` +## 📝 Documentation + +Just make sure you run CMake with the `-DBUILD_DOC=ON` flag. + +The Makefile `all` target will automatically build the documentation. + ## 📎 Some examples Here is a basic example of how you could use the hashmap. @@ -70,7 +77,7 @@ int main(int argc, const char *argv[]) { bool success; Person person = {"James", true}; - // Create a controller with the separate chaining method + // Create a controller with the open addressing method th_t th = th_create(TH_OPEN_ADRESSING); // Insert a new key value pair diff --git a/default.nix b/default.nix index 9649823..6288c3b 100644 --- a/default.nix +++ b/default.nix @@ -2,9 +2,12 @@ stdenv, lib, cmake, + doxygen, + graphviz, enableStatic ? false, enableShared ? !enableStatic, enaleTests ? true, + enableDoc ? true, }: assert enableShared || enableStatic; @@ -17,16 +20,30 @@ stdenv.mkDerivation (finalAttrs: { cmakeFlags = lib.optional enaleTests "-DBUILD_TESTS=ON" - ++ lib.optional enableStatic "-DBUILD_STATIC=ON"; + ++ lib.optional enableStatic "-DBUILD_STATIC=ON" + ++ lib.optional enableDoc '' + -DBUILD_DOC=ON + -DDOT_BIN_PATH=${graphviz}/bin/dot + ''; nativeBuildInputs = [ cmake ]; - postBuild = '' + buildInputs = [ doxygen ]; + + postBuild = lib.optionals enaleTests '' make test ''; + postInstall = lib.optionals enaleTests '' + mkdir $out/doc + + for dir in "html" "latex"; do + mv doc/$dir $out/doc/$dir + done + ''; + meta = { - description = "Simple C hash table implementation."; + description = "This is a library containing multiple C implementations of hashmap."; homepage = "https://github.com/theobori/${finalAttrs.pname}"; license = lib.licenses.mit; }; diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..a141d21 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,41 @@ +find_package(Doxygen) + + +find_program(DOT_BIN NAMES dot) + +if(DOT_BIN) + set(DOT_BIN_PATH "${DOT_BIN}" CACHE PATH "Path to dot") +else() + set(DOT_BIN_PATH "" CACHE PATH "Path to dot") +endif() + +if(DOXYGEN_FOUND) + message(STATUS "Doxygen found: ${DOXYGEN_EXECUTABLE} -- ${DOXYGEN_VERSION}") + + # set Doxygen input and output files. + set(DOXYGEN_INPUT_DIR ${PROJECT_SOURCE_DIR}/src) + set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doxygen) + set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/xml/index.xml) + set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) + set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + + # Generate DoxyFile from the input file. + configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) + + # Create Output directory. + file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) + + + # Command for generating doc from Doxygen config file. + add_custom_command(OUTPUT ${DOXYGEN_INDEX_FILE} + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT} + MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN} + COMMENT "Generating Doxygen documentation" + VERBATIM) + + + # Create CMake Target for generating doc. + add_custom_target(docs ALL DEPENDS ${DOXYGEN_INDEX_FILE}) + +endif() diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 0000000..6a39c51 --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,35 @@ +PROJECT_NAME = "Tinyhash" +PROJECT_BRIEF = "This is a library containing multiple C implementations of hashmap." +EXTRACT_ALL = YES +DISTRIBUTE_GROUP_DOC = YES +EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = YES +EXTRACT_STATIC = YES +EXTRACT_ANON_NSPACES = YES +WARN_NO_PARAMDOC = YES +RECURSIVE = YES +SOURCE_BROWSER = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = NO +GENERATE_TREEVIEW = NO + +HAVE_DOT = YES +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_PATH = "@DOT_BIN_PATH@" + +CALL_GRAPH = YES +CALLER_GRAPH = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +DIRECTORY_GRAPH = YES +DOT_CLEANUP = YES + +INPUT = "@DOXYGEN_INPUT_DIR@" @PROJECT_SOURCE_DIR@/README.md +USE_MDFILE_AS_MAINPAGE = "@PROJECT_SOURCE_DIR@/README.md" diff --git a/flake.nix b/flake.nix index fc157a5..5c6a4f8 100644 --- a/flake.nix +++ b/flake.nix @@ -20,7 +20,7 @@ packages = forEachSupportedSystem ( { pkgs }: { - default = pkgs.callPackage ./. { inherit (pkgs) cmake; }; + default = pkgs.callPackage ./. { }; } ); @@ -31,6 +31,8 @@ packages = with pkgs; [ cmake clang + doxygen + graphviz ]; }; } diff --git a/src/common/hash.c b/src/common/hash.c index 177e22c..ff5d4a6 100644 --- a/src/common/hash.c +++ b/src/common/hash.c @@ -1,12 +1,22 @@ #include #include +/** + * @brief Initial value for hash function. + * + */ #define TH_HASH_INITIAL_VALUE 2166136261u + +/** + * @brief Multiplier for hash function. + * + */ #define TH_HASH_MUL_VALUE 16777619 uint32_t th_hash(uint8_t *bytes, size_t size) { - if (bytes == NULL) + if (bytes == NULL) { return 0; + } uint32_t hash = TH_HASH_INITIAL_VALUE; diff --git a/src/common/hash.h b/src/common/hash.h index cdedca0..f537da1 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -4,6 +4,13 @@ #include #include +/** + * @brief Compute an unsigned int from bytes. + * + * @param bytes + * @param size + * @return uint32_t + */ uint32_t th_hash(uint8_t *bytes, size_t size); #endif diff --git a/src/common/key.c b/src/common/key.c index 5218541..0467c62 100644 --- a/src/common/key.c +++ b/src/common/key.c @@ -1,8 +1,7 @@ -#include "key.h" - #include #include "hash.h" +#include "key.h" th_key_t th_key_create(th_any_t data, size_t size) { return (th_key_t){ @@ -13,8 +12,13 @@ th_key_t th_key_create(th_any_t data, size_t size) { } bool th_key_is_equal(th_key_t *first, th_key_t *second) { - if (first->size != second->size) + if (first == NULL || second == NULL) { + return false; + } + + if (first->size != second->size) { return false; + } return memcmp(first->data, second->data, first->size) == 0; } diff --git a/src/common/key.h b/src/common/key.h index f208b2f..cdeebe7 100644 --- a/src/common/key.h +++ b/src/common/key.h @@ -7,14 +7,34 @@ #include "types.h" +/** + * @brief Represent an entry key. + * + */ typedef struct { uint32_t hash; size_t size; th_any_t data; } th_key_t; +/** + * @brief Create a key struct from data and size, it will automatically + * compute its hash. + * + * @param data + * @param size + * @return th_key_t + */ th_key_t th_key_create(th_any_t data, size_t size); +/** + * @brief Key comparator function. + * + * @param first + * @param second + * @return true + * @return false + */ bool th_key_is_equal(th_key_t *first, th_key_t *second); #endif diff --git a/src/common/table.h b/src/common/table.h index c0bf2e0..8d499ad 100644 --- a/src/common/table.h +++ b/src/common/table.h @@ -1,6 +1,10 @@ #ifndef __TINYHASH_COMMON_TABLE_H__ #define __TINYHASH_COMMON_TABLE_H__ +/** + * @brief Generate the next capacity value absed on a previous one. + * + */ #define TH_TABLE_NEXT_CAPACITY(capacity) (capacity) == 0 ? 8 : (capacity) * 2 #endif diff --git a/src/common/types.h b/src/common/types.h index 67e856b..c92b4de 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -1,8 +1,16 @@ #ifndef __TINYHASH_COMMON_TYPES_H__ #define __TINYHASH_COMMON_TYPES_H__ +/** + * @brief Represent any type of data. + * + */ typedef void *th_any_t; +/** + * @brief Represents any table. + * + */ typedef void *th_generic_table_t; #endif diff --git a/src/open_addressing/entry.h b/src/open_addressing/entry.h index df078d4..219f320 100644 --- a/src/open_addressing/entry.h +++ b/src/open_addressing/entry.h @@ -8,6 +8,10 @@ #include "../common/hash.h" #include "../common/key.h" +/** + * @brief Represent an entry within a bucket. + * + */ typedef struct th_oa_entry_s { th_key_t *key; th_any_t value; diff --git a/src/open_addressing/table.c b/src/open_addressing/table.c index be39646..58157eb 100644 --- a/src/open_addressing/table.c +++ b/src/open_addressing/table.c @@ -17,11 +17,18 @@ void th_oa_table_init(th_oa_table_t *table) { table->entries = NULL; } +/** + * @brief Allocate then initialize an open addressing table. + * It can return NULL. + * + * @return th_oa_table_t* + */ static th_oa_table_t *_th_oa_table_create() { th_oa_table_t *table = malloc(sizeof(th_oa_table_t)); - if (table == NULL) + if (table == NULL) { return NULL; + } th_oa_table_init(table); @@ -32,24 +39,42 @@ th_generic_table_t th_oa_table_create() { return (th_generic_table_t)_th_oa_table_create(); } +/** + * @brief Copy and rehash every value from a table to another. + * Return true on success. + * + * @param dest + * @param src + * @return true + * @return false + */ static bool th_oa_table_copy(th_oa_table_t *dest, th_oa_table_t *src) { bool success; for (int i = 0; i < src->capacity; i++) { th_oa_entry_t *entry = &src->entries[i]; - if (entry->key == NULL) + if (entry->key == NULL) { continue; + } success = th_oa_table_put_with_key(dest, entry->key, entry->value); - if (success == false) + if (success == false) { return false; + } } return true; } +/** + * @brief Increase the size of a table. + * + * @param table + * @return true + * @return false + */ static bool th_oa_table_increase(th_oa_table_t *table) { th_oa_table_t new_table; bool success; @@ -61,14 +86,16 @@ static bool th_oa_table_increase(th_oa_table_t *table) { size_t size = sizeof(th_oa_entry_t) * new_table.capacity; new_table.entries = malloc(size); - if (new_table.entries == NULL) + if (new_table.entries == NULL) { return false; + } memset(new_table.entries, 0, size); success = th_oa_table_copy(&new_table, table); - if (success == false) + if (success == false) { return false; + } th_oa_table_free(table); @@ -77,6 +104,14 @@ static bool th_oa_table_increase(th_oa_table_t *table) { return true; } +/** + * @brief Returns a bucket (entry) depending on a key. + * It can return a tomstone or an empty bucket. + * + * @param table + * @param key + * @return th_oa_entry_t* + */ static th_oa_entry_t *th_oa_table_find(th_oa_table_t *table, th_key_t *key) { int index = key->hash % table->capacity; @@ -88,8 +123,9 @@ static th_oa_entry_t *th_oa_table_find(th_oa_table_t *table, th_key_t *key) { if (entry->is_tombstone == false) { return tombstone != NULL ? tombstone : entry; } else { - if (tombstone == NULL) + if (tombstone == NULL) { tombstone = entry; + } } } else if (th_key_is_equal(key, entry->key) == true) { return entry; @@ -102,29 +138,42 @@ static th_oa_entry_t *th_oa_table_find(th_oa_table_t *table, th_key_t *key) { th_any_t th_oa_table_get(th_generic_table_t generic_table, th_any_t data, size_t data_size) { th_oa_table_t *table = (th_oa_table_t *)generic_table; - if (table->capacity == 0) + if (table->capacity == 0) { return NULL; + } th_key_t key = th_key_create(data, data_size); th_oa_entry_t *entry = th_oa_table_find(table, &key); - if (entry->key == NULL) + if (entry->key == NULL) { return NULL; + } return entry->value; } +/** + * @brief Insert a value within the table with an already existing key. + * + * @param table + * @param key + * @param value + * @return true + * @return false + */ static bool th_oa_table_put_with_key(th_oa_table_t *table, th_key_t *key, th_any_t value) { - if (table->count >= (table->capacity * TH_OA_LOAD_FACTOR)) { - if (th_oa_table_increase(table) == false) + if (table->count >= table->capacity * TH_OA_LOAD_FACTOR) { + if (th_oa_table_increase(table) == false) { return false; + } } th_oa_entry_t *entry = th_oa_table_find(table, key); if (entry->key == NULL) { - if (entry->is_tombstone == false) + if (entry->is_tombstone == false) { table->count++; + } } else { free(entry->key); } @@ -139,6 +188,10 @@ static bool th_oa_table_put_with_key(th_oa_table_t *table, th_key_t *key, bool th_oa_table_put(th_generic_table_t generic_table, th_any_t data, size_t data_size, th_any_t value) { + if (generic_table == NULL) { + return NULL; + } + th_oa_table_t *table = (th_oa_table_t *)generic_table; th_key_t key = th_key_create(data, data_size); @@ -148,14 +201,16 @@ bool th_oa_table_put(th_generic_table_t generic_table, th_any_t data, bool th_oa_table_delete(th_generic_table_t generic_table, th_any_t data, size_t data_size) { th_oa_table_t *table = (th_oa_table_t *)generic_table; - if (table->capacity == 0) + if (table->capacity == 0) { return false; + } th_key_t key = th_key_create(data, data_size); th_oa_entry_t *entry = th_oa_table_find(table, &key); - if (entry->key == NULL) + if (entry->key == NULL) { return false; + } free(entry->key); @@ -171,8 +226,9 @@ void th_oa_table_free(th_generic_table_t generic_table) { for (int i = 0; i < table->capacity; i++) { th_oa_entry_t *entry = &table->entries[i]; - if (entry->key == NULL) + if (entry->key == NULL) { continue; + } free(entry->key); } diff --git a/src/open_addressing/table.h b/src/open_addressing/table.h index 5c8a2e4..aac6f75 100644 --- a/src/open_addressing/table.h +++ b/src/open_addressing/table.h @@ -8,6 +8,10 @@ #include "../common/types.h" #include "entry.h" +/** + * @brief Load factor. + * + */ #define TH_OA_LOAD_FACTOR 0.75 typedef struct { @@ -16,18 +20,63 @@ typedef struct { th_oa_entry_t *entries; } th_oa_table_t; +/** + * @brief Initialize open adressing table values. + * + * @param table + */ void th_oa_table_init(th_oa_table_t *table); +/** + * @brief Return an allocated open addressing table struct. + * + * @return th_generic_table_t + */ th_generic_table_t th_oa_table_create(); +/** + * @brief Get a value from an open addressing table. + * Return NULL if it does exist. + * + * @param table + * @param data + * @param data_size + * @return th_any_t + */ th_any_t th_oa_table_get(th_generic_table_t table, th_any_t data, size_t data_size); +/** + * @brief Insert a value within an open addressing table. + * Return true on success. + * + * @param table + * @param data + * @param data_size + * @param value + * @return true + * @return false + */ bool th_oa_table_put(th_generic_table_t table, th_any_t data, size_t data_size, th_any_t value); +/** + * @brief Free an open addressing table. + * + * @param table + */ void th_oa_table_free(th_generic_table_t table); +/** + * @brief Delete a key value pair in an open addressing table. + * Return true on success. + * + * @param table + * @param data + * @param data_size + * @return true + * @return false + */ bool th_oa_table_delete(th_generic_table_t table, th_any_t data, size_t data_size); diff --git a/src/separate_chaining/entry.c b/src/separate_chaining/entry.c index 9721362..e20765e 100644 --- a/src/separate_chaining/entry.c +++ b/src/separate_chaining/entry.c @@ -1,10 +1,18 @@ #include "entry.h" +/** + * @brief Allocate then initialize a new entry. + * + * @param key + * @param value + * @return th_sc_entry_t* + */ static th_sc_entry_t *th_sc_entry_new(th_key_t *key, th_any_t value) { th_sc_entry_t *entry = malloc(sizeof(th_sc_entry_t)); - if (entry == NULL) + if (entry == NULL) { return NULL; + } entry->key = *key; entry->value = value; @@ -17,11 +25,13 @@ static th_sc_entry_t *th_sc_entry_new(th_key_t *key, th_any_t value) { bool th_sc_entry_add(th_sc_entry_t **root, th_key_t *key, th_any_t value) { th_sc_entry_t *entry = th_sc_entry_new(key, value); - if (entry == NULL) + if (entry == NULL) { return false; + } - if (*root != NULL) + if (*root != NULL) { (*root)->previous = entry; + } entry->next = *root; *root = entry; diff --git a/src/separate_chaining/entry.h b/src/separate_chaining/entry.h index efcc23e..ed905d3 100644 --- a/src/separate_chaining/entry.h +++ b/src/separate_chaining/entry.h @@ -8,6 +8,10 @@ #include "../common/hash.h" #include "../common/key.h" +/** + * @brief Represents a separate chaining entry. + * + */ typedef struct th_sc_entry_s { th_key_t key; th_any_t value; @@ -15,6 +19,16 @@ typedef struct th_sc_entry_s { struct th_sc_entry_s *next; } th_sc_entry_t; +/** + * @brief Add an a separate chaining entry to the beginning of a linked list. + * Return true on success. + * + * @param root + * @param key + * @param value + * @return true + * @return false + */ bool th_sc_entry_add(th_sc_entry_t **root, th_key_t *key, th_any_t value); #endif diff --git a/src/separate_chaining/table.c b/src/separate_chaining/table.c index 14924ec..101804d 100644 --- a/src/separate_chaining/table.c +++ b/src/separate_chaining/table.c @@ -16,11 +16,17 @@ void th_sc_table_init(th_sc_table_t *table) { table->entries = NULL; } +/** + * @brief Allocate then return a new table. + * + * @return th_sc_table_t* + */ static th_sc_table_t *_th_sc_table_create() { th_sc_table_t *table = malloc(sizeof(th_sc_table_t)); - if (table == NULL) + if (table == NULL) { return NULL; + } th_sc_table_init(table); @@ -31,6 +37,15 @@ th_generic_table_t th_sc_table_create() { return (th_generic_table_t)_th_sc_table_create(); } +/** + * @brief Copy and rehash every value from a table to another. + * Return true on success. + * + * @param dest + * @param src + * @return true + * @return false + */ static bool th_sc_table_copy(th_sc_table_t *dest, th_sc_table_t *src) { bool success; @@ -40,8 +55,9 @@ static bool th_sc_table_copy(th_sc_table_t *dest, th_sc_table_t *src) { while (entry != NULL) { success = th_sc_table_put_with_key(dest, &entry->key, entry->value); - if (success == false) + if (success == false) { return false; + } entry = entry->next; } @@ -50,6 +66,13 @@ static bool th_sc_table_copy(th_sc_table_t *dest, th_sc_table_t *src) { return true; } +/** + * @brief Increase the size of a table. + * + * @param table + * @return true + * @return false + */ static bool th_sc_table_increase(th_sc_table_t *table) { th_sc_table_t new_table; bool success; @@ -61,14 +84,16 @@ static bool th_sc_table_increase(th_sc_table_t *table) { size_t size = sizeof(th_sc_entry_t *) * new_table.capacity; new_table.entries = malloc(size); - if (new_table.entries == NULL) + if (new_table.entries == NULL) { return false; + } memset(new_table.entries, 0, size); success = th_sc_table_copy(&new_table, table); - if (success == false) + if (success == false) { return false; + } th_sc_table_free((th_generic_table_t)table); @@ -77,10 +102,19 @@ static bool th_sc_table_increase(th_sc_table_t *table) { return true; } +/** + * @brief Returns a bucket (entry) depending on a key. + * Return NULL if the entry does not exist. + * + * @param table + * @param key + * @return th_sc_entry_t* + */ static th_sc_entry_t *th_sc_table_find(th_sc_table_t *table, th_key_t *key) { // Avoid division by 0 - if (table->capacity == 0) + if (table->capacity == 0) { return NULL; + } int index = key->hash % table->capacity; th_sc_entry_t *entry = table->entries[index]; @@ -102,17 +136,28 @@ th_any_t th_sc_table_get(th_generic_table_t generic_table, th_any_t data, th_key_t key = th_key_create(data, data_size); th_sc_entry_t *entry = th_sc_table_find(table, &key); - if (entry == NULL) + if (entry == NULL) { return NULL; + } return entry->value; } +/** + * @brief Insert a value within the table with an already existing key. + * + * @param table + * @param key + * @param value + * @return true + * @return false + */ static bool th_sc_table_put_with_key(th_sc_table_t *table, th_key_t *key, th_any_t value) { if (table->count >= table->capacity) { - if (th_sc_table_increase(table) == false) + if (th_sc_table_increase(table) == false) { return false; + } } th_sc_entry_t *entry = th_sc_table_find(table, key); @@ -121,10 +166,13 @@ static bool th_sc_table_put_with_key(th_sc_table_t *table, th_key_t *key, int index = key->hash % table->capacity; th_sc_entry_t **bucket = &table->entries[index]; - if (*bucket == NULL) + if (*bucket == NULL) { table->count++; - if (th_sc_entry_add(bucket, key, value) == false) + } + + if (th_sc_entry_add(bucket, key, value) == false) { return false; + } } else { entry->value = value; } @@ -146,8 +194,9 @@ bool th_sc_table_delete(th_generic_table_t generic_table, th_any_t data, th_key_t key = th_key_create(data, data_size); th_sc_entry_t *entry = th_sc_table_find(table, &key); - if (entry == NULL) + if (entry == NULL) { return false; + } int index = entry->key.hash % table->capacity; th_sc_entry_t **bucket = &table->entries[index]; diff --git a/src/separate_chaining/table.h b/src/separate_chaining/table.h index 2c38c6e..9244472 100644 --- a/src/separate_chaining/table.h +++ b/src/separate_chaining/table.h @@ -8,24 +8,73 @@ #include "../common/types.h" #include "entry.h" +/** + * @brief Represent a separate chaining table. + * + */ typedef struct { uint32_t count; uint32_t capacity; th_sc_entry_t **entries; } th_sc_table_t; +/** + * @brief Initialize a separate chaining table. + * + * @param table + */ void th_sc_table_init(th_sc_table_t *table); +/** + * @brief Return an allocated separate chaining table. + * + * @return th_generic_table_t + */ th_generic_table_t th_sc_table_create(); +/** + * @brief Get a value from an separate chaining table. + * Return NULL if it does exist. + * + * @param table + * @param data + * @param data_size + * @return th_any_t + */ th_any_t th_sc_table_get(th_generic_table_t table, th_any_t data, size_t data_size); +/** + * @brief Insert a value within an separate chaining table. + * Return true on success. + * + * @param table + * @param data + * @param data_size + * @param value + * @return true + * @return false + */ bool th_sc_table_put(th_generic_table_t table, th_any_t data, size_t data_size, th_any_t value); +/** + * @brief Free an separate chaining table. + * + * @param table + */ void th_sc_table_free(th_generic_table_t table); +/** + * @brief Delete a key value pair in an separate chaining table. + * Return true on success. + * + * @param table + * @param data + * @param data_size + * @return true + * @return false + */ bool th_sc_table_delete(th_generic_table_t table, th_any_t data, size_t data_size); diff --git a/src/tinyhash.c b/src/tinyhash.c index 66f5537..7277fd6 100644 --- a/src/tinyhash.c +++ b/src/tinyhash.c @@ -3,6 +3,11 @@ #include "./open_addressing/table.h" #include "./separate_chaining/table.h" +/** + * @brief Static array containing functions that accomplish hashmap operation + * associated with an implementation method. + * + */ static th_funcs_t th_funcs[] = { [TH_SEPARATE_CHAINING] = { @@ -23,7 +28,15 @@ static th_funcs_t th_funcs[] = { }, }; +static size_t th_funcs_length = sizeof(th_funcs) / sizeof(th_funcs[0]); + +th_t th_create_default() { return th_create(TH_SEPARATE_CHAINING); } + th_t th_create(th_method_t method) { + if (method < 0 || method >= th_funcs_length) { + return th_create_default(); + } + th_funcs_t funcs = th_funcs[method]; return (th_t){ @@ -33,8 +46,6 @@ th_t th_create(th_method_t method) { }; } -th_t th_create_default() { return th_create(TH_SEPARATE_CHAINING); } - th_any_t th_get(th_t *th, th_any_t data, size_t data_size) { return th->funcs.get(th->table, data, data_size); } diff --git a/src/tinyhash.h b/src/tinyhash.h index 7040f29..9fa1e9b 100644 --- a/src/tinyhash.h +++ b/src/tinyhash.h @@ -6,21 +6,50 @@ #include "./common/types.h" +/** + * @brief Implementation methods. + * + */ typedef enum { TH_SEPARATE_CHAINING, TH_OPEN_ADRESSING, } th_method_t; +/** + * @brief Pointer on generic function to create a table. + * + */ typedef th_generic_table_t (*th_create_func_t)(void); +/** + * @brief Pointer on generic function to get a value from a table. + * + */ typedef th_any_t (*th_get_func_t)(th_generic_table_t, th_any_t, size_t); +/** + * @brief Pointer on generic function to insert a value into a table. + * + */ typedef bool (*th_put_func_t)(th_generic_table_t, th_any_t, size_t, th_any_t); +/** + * @brief Pointer on generic function to delete a value from a table. + * + */ typedef bool (*th_delete_func_t)(th_generic_table_t, th_any_t, size_t); +/** + * @brief Pointer on generic function to free a table. + * + */ typedef void (*th_free_func_t)(th_generic_table_t); +/** + * @brief Centralizing every function associated with + * an unique implementation method. + * + */ typedef struct { th_create_func_t create; th_get_func_t get; @@ -29,22 +58,74 @@ typedef struct { th_free_func_t _free; } th_funcs_t; +/** + * @brief Represent a hashmap controller. + * + */ typedef struct { th_method_t method; th_funcs_t funcs; th_generic_table_t table; } th_t; +/** + * @brief Allocate then initialize a hashmap controller. + * based on the given method. + * + * @param method + * @return th_t + */ th_t th_create(th_method_t method); +/** + * @brief Allocate then initialize a hashmap controller + * with its default values. + * + * @return th_t + */ th_t th_create_default(); +/** + * @brief Returns a value from a hashmap. + * Return NULL if it doest not exist. + * + * @param th + * @param data + * @param data_size + * @return th_any_t + */ th_any_t th_get(th_t *th, th_any_t data, size_t data_size); +/** + * @brief Insert element within the hashmap. + * Return true on success. + * + * @param th + * @param data + * @param data_size + * @param value + * @return true + * @return false + */ bool th_put(th_t *th, th_any_t data, size_t data_size, th_any_t value); +/** + * @brief Delete a key value pair from a hashmap. + * Return true on success. + * + * @param th + * @param data + * @param data_size + * @return true + * @return false + */ bool th_delete(th_t *th, th_any_t data, size_t data_size); +/** + * @brief Free a hashmap. + * + * @param th + */ void th_free(th_t *th); #endif diff --git a/tests/test_table.c b/tests/test_table.c index 05d1f60..eb28876 100644 --- a/tests/test_table.c +++ b/tests/test_table.c @@ -180,12 +180,6 @@ MunitResult test_th_delete(const MunitParameter params[], void *data) { th_put(&th, keys[i], strlen(keys[i]), (th_any_t)(uint64_t)i + 1); } - // Check everything exists - for (int i = 0; i < keys_length; i++) { - value = th_get(&th, keys[i], strlen(keys[i])); - munit_assert_not_null(value); - } - ok = th_delete(&th, "a", strlen("a")); munit_assert_true(ok);