From 25e259ae072b56ca8cb35ae26ac3cdf4c6b34220 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 10 Sep 2025 18:38:21 +0200 Subject: [PATCH] feat(hashmap): add size, contains --- hashmap/myhashmap.c | 116 ++++++++++++++++++++++++++++++++++++-------- hashmap/myhashmap.h | 112 ++++++++++++++++++++---------------------- vector/myvector.c | 6 +-- vector/myvector.h | 4 +- 4 files changed, 154 insertions(+), 84 deletions(-) diff --git a/hashmap/myhashmap.c b/hashmap/myhashmap.c index 074f0a3..912478d 100644 --- a/hashmap/myhashmap.c +++ b/hashmap/myhashmap.c @@ -4,10 +4,13 @@ #include #include +/* + * @brief Returns the mutex ID. + */ static size_t get_mutex(hashmap_s *hashmap, size_t hash) { return hash % hashmap->num_locks; } static size_t get_bucket_index(hashmap_s *hashmap, void *key) { - unsigned int hash = hashmap->hash_fn(key); + unsigned int hash = hashmap->hash(key); return hash % MYCLIB_HASHMAP_SIZE; } @@ -17,13 +20,13 @@ static void free_bucket_content(hashmap_s *hashmap, bucket_s *bucket) { } /* Free key if free function is provided */ - if (hashmap->free_key_fn != NULL && bucket->key != NULL) { - hashmap->free_key_fn(bucket->key); + if (hashmap->free_key != NULL && bucket->key != NULL) { + hashmap->free_key(bucket->key); } /* Free value if free function is provided */ - if (hashmap->free_value_fn != NULL && bucket->value != NULL) { - hashmap->free_value_fn(bucket->value); + if (hashmap->free_value != NULL && bucket->value != NULL) { + hashmap->free_value(bucket->value); } } @@ -40,7 +43,7 @@ static bucket_s *find_bucket(hashmap_s *hashmap, void *key, bucket_s **prev) { /* Search through the collision chain */ while (bucket != NULL) { - if (hashmap->equal_fn(bucket->key, key)) { + if (hashmap->equal(bucket->key, key)) { return bucket; } *prev = bucket; @@ -56,13 +59,15 @@ hashmap_s *hm_new(hash_f *hash_fn, equal_f *equal_fn, free_key_f *free_key_fn, f return NULL; } - hashmap->hash_fn = hash_fn; - hashmap->equal_fn = equal_fn; - hashmap->free_key_fn = free_key_fn; - hashmap->free_value_fn = free_value_fn; + hashmap->hash = hash_fn; + hashmap->equal = equal_fn; + hashmap->free_key = free_key_fn; + hashmap->free_value = free_value_fn; hashmap->key_size = key_size; hashmap->value_size = value_size; + hashmap->size = 0; + hashmap->num_locks = 64; hashmap->locks = malloc(sizeof(mtx_t) * hashmap->num_locks); if (hashmap->locks == NULL) { @@ -73,7 +78,7 @@ hashmap_s *hm_new(hash_f *hash_fn, equal_f *equal_fn, free_key_f *free_key_fn, f int ret; for (size_t i = 0; i < hashmap->num_locks; ++i) { - ret = mtx_init(&(hashmap->locks[i]), mtx_plain); + ret = mtx_init(&(hashmap->locks[i]), mtx_recursive); if (ret != thrd_success) { /* Mutex failed */ for (size_t j = 0; j < i; ++j) { @@ -139,17 +144,19 @@ bool hm_set(hashmap_s *hashmap, void *key, void *value) { return false; } - size_t mutex_id = get_mutex(hashmap, hashmap->hash_fn(key)); + size_t mutex_id = get_mutex(hashmap, hashmap->hash(key)); mtx_t *mutex = &(hashmap->locks[mutex_id]); - mtx_lock(mutex); + if (mtx_lock(mutex) != thrd_success) { + return false; + } bucket_s *prev; bucket_s *existing = find_bucket(hashmap, key, &prev); if (existing != NULL) { /* Key exists, update value */ - if (hashmap->free_value_fn != NULL && existing->value != NULL) { - hashmap->free_value_fn(existing->value); + if (hashmap->free_value != NULL && existing->value != NULL) { + hashmap->free_value(existing->value); } existing->value = malloc(hashmap->value_size); @@ -160,6 +167,10 @@ bool hm_set(hashmap_s *hashmap, void *key, void *value) { } memcpy(existing->value, value, hashmap->value_size); + + /* Increase size */ + hashmap->size++; + mtx_unlock(mutex); return true; @@ -187,6 +198,7 @@ bool hm_set(hashmap_s *hashmap, void *key, void *value) { return false; } + hashmap->size++; memcpy(bucket->key, key, hashmap->key_size); memcpy(bucket->value, value, hashmap->value_size); bucket->next = NULL; @@ -220,6 +232,7 @@ bool hm_set(hashmap_s *hashmap, void *key, void *value) { return false; } + hashmap->size++; memcpy(new_bucket->key, key, hashmap->key_size); memcpy(new_bucket->value, value, hashmap->value_size); new_bucket->next = bucket->next; @@ -262,9 +275,11 @@ bucket_s *hm_get(hashmap_s *hashmap, void *key) { return NULL; } - size_t mutex_id = get_mutex(hashmap, hashmap->hash_fn(key)); + size_t mutex_id = get_mutex(hashmap, hashmap->hash(key)); mtx_t *mutex = &(hashmap->locks[mutex_id]); - mtx_lock(mutex); + if (mtx_lock(mutex) != thrd_success) { + return NULL; + } bucket_s *prev; bucket_s *found = find_bucket(hashmap, key, &prev); @@ -287,9 +302,11 @@ bool hm_remove(hashmap_s *hashmap, void *key) { return false; } - size_t mutex_id = get_mutex(hashmap, hashmap->hash_fn(key)); + size_t mutex_id = get_mutex(hashmap, hashmap->hash(key)); mtx_t *mutex = &(hashmap->locks[mutex_id]); - mtx_lock(mutex); + if (mtx_lock(mutex) != thrd_success) { + return false; + } bucket_s *prev; bucket_s *to_remove = find_bucket(hashmap, key, &prev); @@ -325,7 +342,68 @@ bool hm_remove(hashmap_s *hashmap, void *key) { free(to_remove); } + hashmap->size--; + mtx_unlock(mutex); return true; } + +size_t hm_size(hashmap_s *hashmap) { + if (hashmap == NULL) { + return 0; + } + + /* Use the first mutex */ + mtx_t *mutex = &hashmap->locks[0]; + if (mtx_lock(mutex) != thrd_success) { + return 0; + } + + size_t size = hashmap->size; + + mtx_unlock(mutex); + + return size; +} + +bool hm_contains(hashmap_s *hashmap, void *key) { + if (hashmap == NULL || key == NULL) { + return false; + } + + bool res = true; + + size_t mutex_id = get_mutex(hashmap, hashmap->hash(key)); + mtx_t *mutex = &hashmap->locks[mutex_id]; + if (mtx_lock(mutex) != thrd_success) { + return false; + } + + bucket_s *bucket = hm_get(hashmap, key); + if (bucket == NULL) { + res = false; + } else { + hm_free_bucket(bucket); + } + + mtx_unlock(mutex); + + return res; +} + +void hm_foreach(hashmap_s *hashmap, void (*callback)(bucket_s *bucket)) { + if (hashmap == NULL || callback == NULL) { + return; + } + + // TODO +} + +void hm_clear(hashmap_s *hashmap) { + if (hashmap == NULL) { + return; + } + + // TODO +} diff --git a/hashmap/myhashmap.h b/hashmap/myhashmap.h index 590b17a..f492696 100644 --- a/hashmap/myhashmap.h +++ b/hashmap/myhashmap.h @@ -5,13 +5,11 @@ #include #include -#define MYCLIB_HASHMAP_SIZE 1024 /**< Number of buckets in the hash map */ +/**< Number of buckets in the hash map */ +#define MYCLIB_HASHMAP_SIZE 1024 /** - * @brief A single bucket in the hash map - * - * Each bucket can hold one key-value pair and points to the next bucket - * in case of hash collisions (separate chaining). + * @brief A single bucket in the hash map. */ typedef struct bucket { void *key; /**< Pointer to the key */ @@ -20,124 +18,118 @@ typedef struct bucket { } bucket_s; /** - * @brief Function pointer type for a hash function + * @brief Function pointer type for a hash function. * - * @param[in] key Pointer to the key to hash - * @return The computed hash as an unsigned integer + * @param[in] key Pointer to the key to hash. + * @return The computed hash as an unsigned integer. */ typedef unsigned int hash_f(const void *key); /** - * @brief Function pointer type for a key comparison function + * @brief Function pointer type for a key comparison function. * - * @param[in] key_a Pointer to the first key - * @param[in] key_b Pointer to the second key - * @return true if the keys are considered equal, false otherwise + * @param[in] key_a Pointer to the first key. + * @param[in] key_b Pointer to the second key. + * @return true if the keys are considered equal, false otherwise. */ typedef bool equal_f(const void *key_a, const void *key_b); /** - * @brief Function pointer type for freeing a key + * @brief Function pointer type for freeing a key. * - * @param[in] key Pointer to the key to free + * @param[in] key Pointer to the key to free. */ typedef void free_key_f(void *key); /** - * @brief Function pointer type for freeing a value + * @brief Function pointer type for freeing a value. * - * @param[in] value Pointer to the value to free + * @param[in] value Pointer to the value to free. */ typedef void free_value_f(void *value); /** - * @brief Main structure representing the hash map - * - * Contains function pointers for hash computation, key comparison, - * and memory management, along with the bucket array. + * @brief Main structure representing the hash map. */ typedef struct hashmap { - hash_f *hash_fn; /**< Hash function */ - equal_f *equal_fn; /**< Equality comparison function */ - free_key_f *free_key_fn; /**< Key deallocation function (optional) */ - free_value_f *free_value_fn; /**< Value deallocation function (optional) */ + hash_f *hash; /**< Hash function */ + equal_f *equal; /**< Equality comparison function */ + free_key_f *free_key; /**< Key deallocation function (optional) */ + free_value_f *free_value; /**< Value deallocation function (optional) */ size_t key_size; /**< Size in bytes of the key */ size_t value_size; /**< Size in bytes of the value */ bucket_s map[MYCLIB_HASHMAP_SIZE]; /**< Array of bucket chains */ + size_t size; /* Hashmap size (number of keys) */ mtx_t *locks; /**< Mutex array */ size_t num_locks; /**< Number of mutex */ } hashmap_s; /** - * @brief Initialize a new hash map with user-defined behavior functions + * @brief Initialize a new hash map. * - * Creates a new hash map and initializes it with the provided function pointers. - * The free functions can be NULL if no automatic memory management is needed. - * Keys and values will be copied into the hashmap using memcpy with the specified sizes. - * - * @param[in] hash_fn Function used to hash keys (required) - * @param[in] equal_fn Function used to compare keys (required) - * @param[in] free_key_fn Function used to free keys (optional, can be NULL) - * @param[in] free_value_fn Function used to free values (optional, can be NULL) - * @param[in] key_size Size in bytes of each key to be stored - * @param[in] value_size Size in bytes of each value to be stored - * @return A pointer to the newly initialized hash map, or NULL on failure + * @param[in] hash Function used to hash keys. + * @param[in] equal Function used to compare keys. + * @param[in] free_key Function used to free keys (optional, can be NULL). + * @param[in] free_value Function used to free values (optional, can be NULL). + * @param[in] key_size Size in bytes of each key to be stored. + * @param[in] value_size Size in bytes of each value to be stored. + * @return A pointer to the newly initialized hash map, or NULL on failure. */ -hashmap_s *hm_new(hash_f *hash_fn, equal_f *equal_fn, free_key_f *free_key_fn, free_value_f *free_value_fn, size_t key_size, size_t value_size); +hashmap_s *hm_new(hash_f *hash, equal_f *equal, free_key_f *free_key, free_value_f *free_value, size_t key_size, size_t value_size); /** - * @brief Free all resources used by the hash map + * @brief Free all resources used by the hash map. * - * Iterates through all buckets, frees keys and values using the provided - * free functions (if not NULL), and deallocates the hash map structure. - * - * @param[in] hashmap Pointer to the hash map to free + * @param[in] hashmap Hashmap. */ void hm_free(hashmap_s *hashmap); /** - * @brief Free a bucket returned by get + * @brief Free a bucket returned by get. * - * @param[in] bucket Pointer to the bucket to free + * @param[in] bucket Pointer to the bucket */ void hm_free_bucket(bucket_s *bucket); /** - * @brief Insert or update a key-value pair in the hash map + * @brief Insert or update a key-value pair in the hash map. * * If the key already exists, the old value is freed (if free_value_fn is provided) * and replaced with the new value. If the key doesn't exist, a new entry is created. * Both key and value are copied into the hashmap using memcpy. * - * @param[in] hashmap Pointer to the hash map - * @param[in] key Pointer to the key to insert (will be copied, must not be NULL) - * @param[in] value Pointer to the value to insert (will be copied, must not be NULL) - * @return true if the operation succeeded, false on failure (NULL hashmap/key/value or memory allocation failure) + * @param[in] hashmap Pointer to the hash map. + * @param[in] key Pointer to the key to insert (will be copied, must not be NULL). + * @param[in] value Pointer to the value to insert (will be copied, must not be NULL). + * @return true if the operation succeeded, false on failure (NULL hashmap/key/value or memory allocation failure). */ bool hm_set(hashmap_s *hashmap, void *key, void *value); /** - * @brief Retrieve a bucket by key + * @brief Retrieve a bucket by key. * - * Searches for the given key in the hash map and returns the bucket containing it. - * The caller can then access both the key and value from the returned bucket. - * - * @param[in] hashmap Pointer to the hash map - * @param[in] key Pointer to the key to search for - * @return Pointer to the copy of the bucket, to avoid race conditions, or NULL if not found or on invalid input + * @param[in] hashmap Pointer to the hash map. + * @param[in] key Pointer to the key to search for. + * @return Pointer to the copy of the bucket or NULL on failure. + * @note Free after use. */ bucket_s *hm_get(hashmap_s *hashmap, void *key); /** - * @brief Remove a key-value pair from the hash map + * @brief Remove a key-value pair from the hash map. * * Searches for the given key and removes it from the hash map. Both the key * and value are freed using the provided free functions (if not NULL). * - * @param[in] hashmap Pointer to the hash map - * @param[in] key Pointer to the key to remove - * @return true if the key was found and removed, false if not found or on invalid input + * @param[in] hashmap Pointer to the hash map. + * @param[in] key Pointer to the key to remove. + * @return true if the key was found and removed, false otherwise. */ bool hm_remove(hashmap_s *hashmap, void *key); +size_t hm_size(hashmap_s *hashmap); +bool hm_contains(hashmap_s *hashmap, void *key); +void hm_foreach(hashmap_s *hashmap, void (*callback)(bucket_s *bucket)); +void hm_clear(hashmap_s *hashmap); + #endif /* MYCLIB_HASHMAP_H */ diff --git a/vector/myvector.c b/vector/myvector.c index 4c0bcf4..a666606 100644 --- a/vector/myvector.c +++ b/vector/myvector.c @@ -282,8 +282,8 @@ int vec_set(vec_s *vec, size_t index, void *value) { return 0; } -int vec_foreach(vec_s *vec, void (*fefn)(size_t index, void *elem)) { - if (vec == NULL || fefn == NULL) { +int vec_foreach(vec_s *vec, void (*callback)(size_t index, void *elem)) { + if (vec == NULL || callback == NULL) { return -1; } @@ -292,7 +292,7 @@ int vec_foreach(vec_s *vec, void (*fefn)(size_t index, void *elem)) { } for (size_t i = 0; i < vec->size; ++i) { - fefn(i, (char *)vec->data + (i * vec->elem_size)); + callback(i, (char *)vec->data + (i * vec->elem_size)); } mtx_unlock(&vec->lock); diff --git a/vector/myvector.h b/vector/myvector.h index fb054c0..36e146b 100644 --- a/vector/myvector.h +++ b/vector/myvector.h @@ -118,10 +118,10 @@ int vec_clear(vec_s *vec); * @brief Iterate over all elements of the vector. * * @param vec Vector. - * @param fefn Callback function: receives index and element pointer. + * @param callback Receives index and element pointer. * @return 0 on success, -1 on failure. */ -int vec_foreach(vec_s *vec, void (*fefn)(size_t index, void *elem)); +int vec_foreach(vec_s *vec, void (*callback)(size_t index, void *elem)); /** * @brief Sort the vector using qsort().