diff --git a/lib/hash_map.c b/lib/hash_map.c new file mode 100644 index 0000000..0b77d98 --- /dev/null +++ b/lib/hash_map.c @@ -0,0 +1,116 @@ +#include "hash_map.h" + +uint64_t hash(string_t key, size_t capacity) { + uint64_t hash_value = 0; + for (size_t iteration = 0; iteration < capacity; iteration++) { + hash_value += key[iteration]; + hash_value *= key[iteration]; + } + return hash_value % capacity; +} + +struct hash_map *hash_map_initialization() { + struct hash_map *hash_map = malloc(sizeof(struct hash_map)); + hash_map->items = malloc(sizeof(struct linked_list *) * HASH_MAP_INITIAL_CAPACITY); + hash_map->length = 0; + hash_map->capacity = HASH_MAP_INITIAL_CAPACITY; + for (size_t index = 0; index < hash_map->capacity; index++) { + hash_map->items[index] = NULL; + } + return hash_map; +} + +void hash_map_add(struct hash_map *hash_map, string_t key, void *data) { + if (hash_map->length == hash_map->capacity) { + size_t previous_capacity = hash_map->capacity; + hash_map->capacity += HASH_MAP_INITIAL_CAPACITY; + hash_map->items = realloc(hash_map->items, sizeof(struct linked_list *) * hash_map->capacity); + for (size_t index = previous_capacity; index < hash_map->capacity; index++) { + hash_map->items[index] = NULL; + } + } + uint64_t hash_value = hash(key, hash_map->capacity); + struct linked_list *list = hash_map->items[hash_value]; + struct hash_map_item *item = malloc(sizeof(struct hash_map_item)); + item->key = key; + item->data = data; + if (list == NULL) { + list = linked_list_initialization(); + hash_map->items[hash_value] = list; + linked_list_add_in_head(list, (void *)item); + hash_map->length++; + } else { + struct linked_list_node *node = list->head; + bool found = false; + while (node != NULL && !found) { + struct hash_map_item *item = (struct hash_map_item *)node->data; + if (string_equals(key, item->key)) { + item->data = data; + found = true; + } + node = node->next; + } + if (!found) { + linked_list_add_in_head(list, (void *)item); + hash_map->length++; + } + } +} + +void hash_map_remove(struct hash_map *hash_map, string_t key) { + uint64_t hash_value = hash(key, hash_map->capacity); + struct linked_list *list = hash_map->items[hash_value]; + if (list == NULL) { + return; + } + struct linked_list *new_list = linked_list_initialization(); + struct linked_list_node *node = list->head; + while (node != NULL) { + struct hash_map_item *item = (struct hash_map_item *)node->data; + if (!string_equals(key, item->key)) { + linked_list_add_in_head(new_list, item); + } + node = node->next; + } + free(list); + hash_map->items[hash_value] = new_list; + hash_map->length--; +} + +void *hash_map_get(struct hash_map *hash_map, string_t key) { + uint64_t hash_value = hash(key, hash_map->capacity); + struct linked_list *list = hash_map->items[hash_value]; + if (list == NULL) { + return NULL; + } + struct linked_list_node *node = list->head; + while (node != NULL) { + struct hash_map_item *item = (struct hash_map_item *)node->data; + if (string_equals(key, item->key)) { + return item->data; + } + node = node->next; + } + return NULL; +} + +bool hash_map_contains_key(struct hash_map *hash_map, string_t key) { + return hash_map_get(hash_map, key) != NULL; +} + +string_t *hash_map_get_keys(struct hash_map *hash_map) { + string_t *keys = malloc(sizeof(string_t) * hash_map->length); + size_t index = 0; + for (size_t hash_value = 0; hash_value < hash_map->capacity; hash_value++) { + struct linked_list *list = hash_map->items[hash_value]; + if (list != NULL) { + struct linked_list_node *node = list->head; + while (node != NULL) { + struct hash_map_item *item = (struct hash_map_item *)node->data; + keys[index++] = item->key; + node = node->next; + } + } + } + return keys; +} diff --git a/lib/hash_map.h b/lib/hash_map.h new file mode 100644 index 0000000..36b6551 --- /dev/null +++ b/lib/hash_map.h @@ -0,0 +1,89 @@ +#ifndef __LIBCPROJECT_HASH_MAP__ +#define __LIBCPROJECT_HASH_MAP__ + +#include +#include +#include + +#include "linked_list.h" +#include "string.h" +#include "types.h" + +#define HASH_MAP_INITIAL_CAPACITY 10 + +/** + * @brief Hash map data structure. + * @since v2.0.0 + */ +struct hash_map { + struct linked_list **items; + + size_t length; + size_t capacity; +}; + +/** + * @brief Hash map item data structure. + * @since v2.0.0 + */ +struct hash_map_item { + void *data; + string_t key; +}; + +/** + * @brief Hash function. + * @param key + * @param capacity + * @since v2.0.0 + */ +uint64_t hash(string_t key, size_t capacity); + +/** + * @brief Hash map initialization. + * @since v2.0.0 + */ +struct hash_map *hash_map_initialization(); + +/** + * @brief Add an item to the hash map. + * @param hash_map + * @param key + * @param data + * @since v2.0.0 + */ +void hash_map_add(struct hash_map *hash_map, string_t key, void *data); + +/** + * @brief Remove an item from the hash map. + * @param hash_map + * @param key + * @since v2.0.0 + */ +void hash_map_remove(struct hash_map *hash_map, string_t key); + +/** + * @brief Get an item from the hash map. + * @param hash_map + * @param key + * @since v2.0.0 + */ +void *hash_map_get(struct hash_map *hash_map, string_t key); + +/** + * @brief Check if the hash map contains a key. + * @param hash_map + * @param key + * @since v2.0.0 + */ +bool hash_map_contains_key(struct hash_map *hash_map, string_t key); + +/** + * @brief Get the hash map keys. + * + * @param hash_map + * @since v2.0.0 + */ +string_t *hash_map_get_keys(struct hash_map *hash_map); + +#endif diff --git a/lib/terminal.c b/lib/terminal.c index 87d453a..bbdb932 100644 --- a/lib/terminal.c +++ b/lib/terminal.c @@ -109,3 +109,21 @@ void terminal_print_dictionary(struct dictionary* dictionary, void (*print_eleme } printf("}\n"); } + +void terminal_print_hash_map(struct hash_map* hash_map, void (*print_element)(void*)) { + if (hash_map == NULL) { + exit(EXIT_FAILURE); + } + printf("{\n"); + string_t* keys = hash_map_get_keys(hash_map); + for (size_t index = 0; index < hash_map->length; index++) { + string_t key = keys[index]; + void* value = hash_map_get(hash_map, key); + printf("\t\""); + terminal_print_string(key); + printf("\" -> "); + print_element(&value); + printf("\n"); + } + printf("}\n"); +} diff --git a/lib/terminal.h b/lib/terminal.h index 6e8d827..4d07a0b 100644 --- a/lib/terminal.h +++ b/lib/terminal.h @@ -8,6 +8,7 @@ #include "character.h" #include "dictionary.h" +#include "hash_map.h" #include "linked_list.h" #include "queue.h" #include "stack.h" @@ -106,4 +107,13 @@ void terminal_print_linked_list(struct linked_list* linked_list, void (*print_el */ void terminal_print_dictionary(struct dictionary* dictionary, void (*print_element)(void*)); +/** + * @brief Print a hash map. + * + * @param hash_map + * @param print_element + * @since v2.0.0 + */ +void terminal_print_hash_map(struct hash_map* hash_map, void (*print_element)(void*)); + #endif diff --git a/libcproject.h b/libcproject.h index be5758a..eea5aec 100644 --- a/libcproject.h +++ b/libcproject.h @@ -6,6 +6,7 @@ #include "lib/convert.h" #include "lib/dictionary.h" #include "lib/filesystem.h" +#include "lib/hash_map.h" #include "lib/linked_list.h" #include "lib/mathematics.h" #include "lib/queue.h" diff --git a/test/hash_map_test.c b/test/hash_map_test.c new file mode 100644 index 0000000..25411e0 --- /dev/null +++ b/test/hash_map_test.c @@ -0,0 +1,27 @@ +#include "hash_map_test.h" + +void hash_map_test() { + struct hash_map *hash_map = hash_map_initialization(); + assert(hash_map->length == 0); + hash_map_add(hash_map, "key", (void *)'a'); + hash_map_add(hash_map, "key1", (void *)'b'); + hash_map_add(hash_map, "key2", (void *)'c'); + hash_map_add(hash_map, "key3", (void *)'d'); + hash_map_add(hash_map, "key4", (void *)'e'); + hash_map_add(hash_map, "key5", (void *)'f'); + assert(hash_map->length == 6); + assert(hash_map_get(hash_map, "key") == (void *)'a'); + assert(hash_map_get(hash_map, "key1") == (void *)'b'); + assert(hash_map_get(hash_map, "key2") == (void *)'c'); + assert(hash_map_get(hash_map, "key3") == (void *)'d'); + assert(hash_map_get(hash_map, "key4") == (void *)'e'); + assert(hash_map_get(hash_map, "key5") == (void *)'f'); + hash_map_add(hash_map, "key5", (void *)'a'); + assert(hash_map_get(hash_map, "key5") == (void *)'a'); + assert(hash_map_contains_key(hash_map, "key5")); + assert(!hash_map_contains_key(hash_map, "invalid key")); + assert(hash_map_contains_key(hash_map, "key5")); + hash_map_remove(hash_map, "key5"); + assert(hash_map->length == 5); + assert(!hash_map_contains_key(hash_map, "key5")); +} diff --git a/test/hash_map_test.h b/test/hash_map_test.h new file mode 100644 index 0000000..0cd7640 --- /dev/null +++ b/test/hash_map_test.h @@ -0,0 +1,12 @@ +#ifndef __HASH_MAP_TEST__ +#define __HASH_MAP_TEST__ + +#include +#include +#include + +#include "libcproject.h" + +void hash_map_test(); + +#endif diff --git a/test/main.c b/test/main.c index 2ed91a2..574c8e4 100644 --- a/test/main.c +++ b/test/main.c @@ -5,6 +5,7 @@ #include "character_test.h" #include "convert_test.h" #include "dictionary_test.h" +#include "hash_map_test.h" #include "linked_list_test.h" #include "mathematics_test.h" #include "queue_test.h" @@ -16,6 +17,7 @@ int main() { character_test(); convert_test(); dictionary_test(); + hash_map_test(); linked_list_test(); mathematics_test(); queue_test();