diff --git a/include/http/http.h b/include/http/http.h index 914cb21..83c257b 100644 --- a/include/http/http.h +++ b/include/http/http.h @@ -3,8 +3,8 @@ #include +#include "myclib/hashmap/myhashmap.h" #include "utils/config.h" -#include "utils/hashmap.h" #define CWS_HTTP_LOCATION_LEN 512 #define CWS_HTTP_LOCATION_PATH_LEN 1024 @@ -61,7 +61,7 @@ size_t cws_http_response_builder(char **response, char *http_version, cws_http_s size_t body_len_bytes); void cws_http_send_response(cws_http *request, cws_http_status status); -void cws_http_send_resource(cws_http *request); +int cws_http_send_resource(cws_http *request); void cws_http_send_simple_html(cws_http *request, cws_http_status status, char *title, char *description); void cws_http_free(cws_http *request); diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000..7fc76e1 --- /dev/null +++ b/include/meson.build @@ -0,0 +1,2 @@ +myclib = files('myclib/hashmap/myhashmap.c') +myclib += files('myclib/string/mystring.c') diff --git a/include/myclib/hashmap/myhashmap.c b/include/myclib/hashmap/myhashmap.c new file mode 100644 index 0000000..b93e9be --- /dev/null +++ b/include/myclib/hashmap/myhashmap.c @@ -0,0 +1,188 @@ +#include "myhashmap.h" + +#include +#include +#include + +static size_t mcl_get_bucket_index(mcl_hashmap *hashmap, void *key) { + unsigned int hash = hashmap->hash_fn(key); + return hash % MYCLIB_HASHMAP_SIZE; +} + +static void mcl_free_bucket_content(mcl_hashmap *hashmap, mcl_bucket *bucket) { + if (bucket == NULL) { + return; + } + + /* Free key if free function is provided */ + if (hashmap->free_key_fn != NULL && bucket->key != NULL) { + hashmap->free_key_fn(bucket->key); + } + + /* Free value if free function is provided */ + if (hashmap->free_value_fn != NULL && bucket->value != NULL) { + hashmap->free_value_fn(bucket->value); + } +} + +static mcl_bucket *mcl_find_bucket(mcl_hashmap *hashmap, void *key, mcl_bucket **prev) { + size_t index = mcl_get_bucket_index(hashmap, key); + mcl_bucket *bucket = &hashmap->map[index]; + + *prev = NULL; + + /* Return NULL if first bucket is empty */ + if (bucket->key == NULL) { + return NULL; + } + + /* Search through the collision chain */ + while (bucket != NULL) { + if (hashmap->equal_fn(bucket->key, key)) { + return bucket; + } + *prev = bucket; + bucket = bucket->next; + } + + return NULL; +} + +mcl_hashmap *mcl_hm_init(mcl_hash_fn *hash_fn, mcl_equal_fn *equal_fn, mcl_free_key_fn *free_key_fn, mcl_free_value_fn *free_value_fn) { + mcl_hashmap *hashmap = malloc(sizeof(mcl_hashmap)); + if (hashmap == NULL) { + return NULL; + } + + /* Initialize hash map with given parameters */ + hashmap->hash_fn = hash_fn; + hashmap->equal_fn = equal_fn; + hashmap->free_key_fn = free_key_fn; + hashmap->free_value_fn = free_value_fn; + + /* Clear all buckets in the map */ + memset(hashmap->map, 0, sizeof(hashmap->map)); + + return hashmap; +} + +void mcl_hm_free(mcl_hashmap *hashmap) { + if (hashmap == NULL) { + return; + } + + /* Iterate through all buckets in the hash map */ + for (size_t i = 0; i < MYCLIB_HASHMAP_SIZE; ++i) { + mcl_bucket *bucket = &hashmap->map[i]; + + /* Free the first bucket if it contains data */ + if (bucket->key != NULL) { + mcl_free_bucket_content(hashmap, bucket); + } + + /* Free all chained buckets */ + bucket = bucket->next; + while (bucket != NULL) { + mcl_bucket *next = bucket->next; + mcl_free_bucket_content(hashmap, bucket); + free(bucket); + bucket = next; + } + } + + /* Free the hash map structure itself */ + free(hashmap); +} + +bool mcl_hm_set(mcl_hashmap *hashmap, void *key, void *value) { + if (hashmap == NULL || key == NULL) { + return false; + } + + /* Try to find existing bucket */ + mcl_bucket *prev; + mcl_bucket *existing = mcl_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); + } + existing->value = value; + return true; + } + + /* Key doesn't exist, need to insert new bucket */ + size_t index = mcl_get_bucket_index(hashmap, key); + mcl_bucket *bucket = &hashmap->map[index]; + + /* If first bucket is empty, use it */ + if (bucket->key == NULL) { + bucket->key = key; + bucket->value = value; + bucket->next = NULL; + return true; + } + + /* Create new bucket and insert at head of collision chain */ + mcl_bucket *new_bucket = malloc(sizeof(mcl_bucket)); + if (new_bucket == NULL) { + return false; + } + + new_bucket->key = key; + new_bucket->value = value; + new_bucket->next = bucket->next; + bucket->next = new_bucket; + + return true; +} + +mcl_bucket *mcl_hm_get(mcl_hashmap *hashmap, void *key) { + if (hashmap == NULL || key == NULL) { + return NULL; + } + + mcl_bucket *prev; + return mcl_find_bucket(hashmap, key, &prev); +} + +bool mcl_hm_remove(mcl_hashmap *hashmap, void *key) { + if (hashmap == NULL || key == NULL) { + return false; + } + + mcl_bucket *prev; + mcl_bucket *to_remove = mcl_find_bucket(hashmap, key, &prev); + + if (to_remove == NULL) { + return false; + } + + /* Free the content of the bucket */ + mcl_free_bucket_content(hashmap, to_remove); + + /* Handle removal based on position in chain */ + if (prev == NULL) { + /* Removing first bucket in chain */ + if (to_remove->next != NULL) { + /* Move next bucket's content to first bucket and free the next bucket */ + mcl_bucket *next_bucket = to_remove->next; + to_remove->key = next_bucket->key; + to_remove->value = next_bucket->value; + to_remove->next = next_bucket->next; + free(next_bucket); + } else { + /* No next bucket, mark first bucket as empty */ + to_remove->key = NULL; + to_remove->value = NULL; + to_remove->next = NULL; + } + } else { + /* Removing bucket from middle/end of chain */ + prev->next = to_remove->next; + free(to_remove); + } + + return true; +} diff --git a/include/utils/hashmap.h b/include/myclib/hashmap/myhashmap.h similarity index 100% rename from include/utils/hashmap.h rename to include/myclib/hashmap/myhashmap.h diff --git a/include/myclib/string/mystring.c b/include/myclib/string/mystring.c new file mode 100644 index 0000000..2e979fc --- /dev/null +++ b/include/myclib/string/mystring.c @@ -0,0 +1,119 @@ +#include "mystring.h" + +#include +#include +#include + +mcl_string *mcl_string_new(const char *text, long initial_capacity) { + if (!text) { + return NULL; + } + + /* Allocate string struct */ + mcl_string *str = malloc(sizeof(mcl_string)); + if (!str) { + return NULL; + } + + /* Calculate size and capacity */ + str->size = strlen(text); + size_t capacity = initial_capacity; + + if (capacity != -1 && capacity - 1 < str->size) { + return NULL; + } + + if (capacity == -1) { + capacity = (unsigned long)pow(2, (unsigned)log2(str->size) + 1); + } + + str->capacity = capacity; + + /* Allocate data buffer */ + str->data = malloc(sizeof(char) * str->capacity); + if (!str->data) { + free(str); + + return NULL; + } + + /* Copy the text and ensure null termination */ + memset(str->data, 0, str->capacity); + memcpy(str->data, text, str->size); + str->data[str->size] = '\0'; + + return str; +} + +int mcl_string_append(mcl_string *string, const char *text) { + if (!string || !text) { + return -1; + } + + /* Handle empty case */ + size_t text_len = strlen(text); + if (text_len == 0) { + return 0; + } + + size_t new_size = text_len + string->size; + + /* Check if we need to resize */ + if (new_size + 1 > string->capacity) { + size_t new_capacity = (unsigned long)pow(2, (unsigned)log2(new_size) + 1); + /* Reallocate the buffer */ + void *new_data = realloc(string->data, sizeof(char) * new_capacity); + if (!new_data) { + return -1; + } + + string->data = new_data; + string->capacity = new_capacity; + + /* Init to 0 the new capacity */ + memset(string->data + string->size, 0, string->capacity - string->size); + } + + /* Append text */ + memcpy(string->data + string->size, text, text_len); + string->size = new_size; + string->data[string->size] = '\0'; + + return 0; +} + +void mcl_string_free(mcl_string *string) { + if (!string) { + return; + } + + if (string->data) { + free(string->data); + } + + free(string); +} + +size_t mcl_string_length(const mcl_string *string) { + if (!string) { + return 0; + } + + return string->size; +} + +size_t mcl_string_capacity(const mcl_string *string) { + if (!string) { + return 0; + } + + return string->capacity; +} + +const char *mcl_string_cstr(const mcl_string *string) { + if (!string || !string->data) { + return ""; + } + + return string->data; +} diff --git a/include/myclib/string/mystring.h b/include/myclib/string/mystring.h new file mode 100644 index 0000000..f6daa54 --- /dev/null +++ b/include/myclib/string/mystring.h @@ -0,0 +1,73 @@ +#ifndef MYCLIB_STRING_H +#define MYCLIB_STRING_H + +#include + +/** + * @brief String structure + */ +typedef struct mcl_string_t { + size_t size; /**< Length of the string (excluding null terminator) */ + size_t capacity; /**< Total allocated capacity */ + char *data; /**< Pointer to the string data */ +} mcl_string; + +/** + * @brief Create a new string + * + * @param text The text to initialize from + * @param initial_capacity The initial capacity, pass -1 to retrieve it from the text + * @return Pointer to the new string, or NULL on failure + * + * @note The caller is responsible for freeing the returned string with mcl_string_free() and to pass the right inital_capacity + */ +mcl_string *mcl_string_new(const char *text, long initial_capacity); + +/** + * @brief Append text to an existing string + * + * @param string The string to append to + * @param text The string to append (can be empty) + * @return 0 on success, -1 on failure + * + * @note If it fails, the original string remains unchanged + */ +int mcl_string_append(mcl_string *string, const char *text); + +/** + * @brief Free a string + * + * @param string The string to free + * + * @note This function is safe to call with NULL pointers + */ +void mcl_string_free(mcl_string *string); + +/** + * @brief Get the current length of the string + * + * @param string The string to query + * @return The length of the string, or 0 if string is NULL + */ +size_t mcl_string_length(const mcl_string *string); + +/** + * @brief Get the current capacity of the string + * + * @param string The string to query + * @return The capacity of the string buffer, or 0 if string is NULL + */ +size_t mcl_string_capacity(const mcl_string *string); + +/** + * @brief Get a read-only string representation + * + * @param string The string to access + * @return Pointer to null-terminated string data, or empty string "" if string is NULL + * + * @warning The returned pointer should not be modified directly and may become + * invalid after any modification operation on the string + */ +const char *mcl_string_cstr(const mcl_string *string); + +#endif /* MYCLIB_STRING_H */ diff --git a/include/server/server.h b/include/server/server.h index 58acd54..b32b9de 100644 --- a/include/server/server.h +++ b/include/server/server.h @@ -7,8 +7,8 @@ #include #include +#include "myclib/hashmap/myhashmap.h" #include "utils/config.h" -#include "utils/hashmap.h" /* Clients max queue */ #define CWS_SERVER_BACKLOG 10 @@ -89,4 +89,7 @@ int cws_server_accept_client(int sockfd, struct sockaddr_storage *their_sa, sock */ void cws_server_close_client(int epfd, int client_fd, mcl_hashmap *hashmap); +int cws_server_handle_new_client(int sockfd, int epfd, mcl_hashmap *clients); +int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_config *config); + #endif diff --git a/include/utils/utils.h b/include/utils/utils.h index f533420..bde43d0 100644 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -39,6 +39,6 @@ void my_str_free_fn(void *value); unsigned int my_int_hash_fn(const void *key); bool my_int_equal_fn(const void *a, const void *b); -void my_int_free_fn(void *value); +void my_int_free_key_fn(void *key); #endif diff --git a/meson.build b/meson.build index df1ba8f..c9288c6 100644 --- a/meson.build +++ b/meson.build @@ -1,15 +1,19 @@ project('cws', 'c', version : '0.1.0') +cc = meson.get_compiler('c') + subdir('src') +subdir('include') incdir = include_directories('include') libssl = dependency('libssl') libyaml = dependency('yaml-0.1') libcyaml = dependency('libcyaml') -deps = [libssl, libyaml, libcyaml] +libmath = cc.find_library('m', required : true) +deps = [libssl, libyaml, libcyaml, libmath] add_global_arguments('-DUSE_COLORS', language : 'c') add_global_arguments('-DEVELOPER', language : 'c') -executable('cws', server, include_directories : incdir, dependencies : deps) +executable('cws', server + myclib, include_directories : incdir, dependencies : deps) diff --git a/src/http/http.c b/src/http/http.c index 0f87dc6..cac5ab2 100644 --- a/src/http/http.c +++ b/src/http/http.c @@ -191,11 +191,11 @@ void cws_http_send_response(cws_http *request, cws_http_status status) { } } -void cws_http_send_resource(cws_http *request) { +int cws_http_send_resource(cws_http *request) { FILE *file = fopen(request->location_path, "rb"); if (file == NULL) { cws_http_send_response(request, CWS_HTTP_NOT_FOUND); - return; + return -1; } /* Retrieve correct Content-Type */ @@ -215,7 +215,7 @@ void cws_http_send_resource(cws_http *request) { if (file_data == NULL) { fclose(file); CWS_LOG_ERROR("Unable to allocate file data"); - return; + return -1; } /* Read 1 byte until content_length from file and put in file_data */ @@ -225,7 +225,7 @@ void cws_http_send_resource(cws_http *request) { if (read_bytes != content_length) { free(file_data); CWS_LOG_ERROR("Partial read from file"); - return; + return -1; } char conn[32] = "close"; @@ -238,13 +238,16 @@ void cws_http_send_resource(cws_http *request) { size_t response_len = cws_http_response_builder(&response, "HTTP/1.1", CWS_HTTP_OK, content_type, conn, file_data, content_length); size_t bytes_sent = 0; + size_t sent; do { - size_t sent = send(request->sockfd, response + bytes_sent, response_len, 0); + sent = send(request->sockfd, response + bytes_sent, response_len, 0); bytes_sent += sent; - } while (bytes_sent < response_len); + } while (bytes_sent < response_len && sent != 0); free(response); free(file_data); + + return 0; } int cws_http_get_content_type(cws_http *request, char *content_type) { diff --git a/src/meson.build b/src/meson.build index 59f2b66..cda4eaf 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,3 +1,3 @@ server = files('main.c', 'server/server.c') -server += files('utils/utils.c', 'utils/hashmap.c', 'utils/config.c') +server += files('utils/utils.c', 'utils/config.c') server += files('http/http.c') diff --git a/src/server/server.c b/src/server/server.c index dfe7d5a..e099703 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -81,15 +81,12 @@ int cws_server_start(cws_config *config) { } int cws_server_loop(int sockfd, cws_config *config) { - struct sockaddr_storage their_sa; - socklen_t theirsa_size = sizeof their_sa; - int ret; - - mcl_hashmap *clients = mcl_hm_init(my_int_hash_fn, my_int_equal_fn, NULL, my_int_free_fn); + mcl_hashmap *clients = mcl_hm_init(my_int_hash_fn, my_int_equal_fn, my_int_free_key_fn, my_str_free_fn); if (!clients) { return -1; } + int ret = 0; int epfd = epoll_create1(0); ret = cws_fd_set_nonblocking(sockfd); @@ -116,69 +113,20 @@ int cws_server_loop(int sockfd, cws_config *config) { return -1; } - int client_fd; - while (cws_server_run) { int nfds = epoll_wait(epfd, revents, CWS_SERVER_EPOLL_MAXEVENTS, CWS_SERVER_EPOLL_TIMEOUT); for (int i = 0; i < nfds; ++i) { if (revents[i].data.fd == sockfd) { - /* New client */ - char ip[INET_ADDRSTRLEN]; - - client_fd = cws_server_accept_client(sockfd, &their_sa, &theirsa_size); - if (client_fd < 0) { - continue; + ret = cws_server_handle_new_client(sockfd, epfd, clients); + if (ret < 0) { + CWS_LOG_DEBUG("handle new client error"); } - - cws_utils_get_client_ip(&their_sa, ip); - CWS_LOG_INFO("Client (%s) connected", ip); - - cws_fd_set_nonblocking(client_fd); - cws_epoll_add(epfd, client_fd, EPOLLIN); - mcl_hm_set(clients, &client_fd, &their_sa); } else { - char data[4096] = {0}; - char ip[INET_ADDRSTRLEN] = {0}; - - /* Incoming data */ - client_fd = revents[i].data.fd; - const ssize_t bytes_read = recv(client_fd, data, sizeof data, 0); - - /* Retrieve client ip */ - mcl_bucket *client = mcl_hm_get(clients, &client_fd); - struct sockaddr_storage client_sas = *(struct sockaddr_storage *)client->value; - cws_utils_get_client_ip(&client_sas, ip); - - if (bytes_read == 0) { - /* Client disconnected */ - CWS_LOG_INFO("Client (%s) disconnected", ip); - cws_server_close_client(epfd, client_fd, clients); - continue; + ret = cws_server_handle_client_data(revents[i].data.fd, epfd, clients, config); + if (ret < 0) { + CWS_LOG_DEBUG("handle client data error"); } - - if (bytes_read < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - /* Error during read, handle it (close client) */ - CWS_LOG_INFO("Client (%s) disconnected (error)", ip); - cws_server_close_client(epfd, client_fd, clients); - } - continue; - } - - /* Parse HTTP request */ - cws_http *request = cws_http_parse(data, client_fd, config); - - if (request == NULL) { - cws_server_close_client(epfd, client_fd, clients); - continue; - } - - cws_http_send_resource(request); - cws_http_free(request); - - /* Clear str */ - memset(data, 0, sizeof data); } } } @@ -187,7 +135,90 @@ int cws_server_loop(int sockfd, cws_config *config) { free(revents); close(epfd); mcl_hm_free(clients); - CWS_LOG_INFO("Closing..."); + + return 0; +} + +int cws_server_handle_new_client(int sockfd, int epfd, mcl_hashmap *clients) { + struct sockaddr_storage their_sa; + socklen_t theirsa_size = sizeof their_sa; + char ip[INET_ADDRSTRLEN]; + + int client_fd = cws_server_accept_client(sockfd, &their_sa, &theirsa_size); + if (client_fd < 0) { + return -1; + } + + cws_utils_get_client_ip(&their_sa, ip); + CWS_LOG_INFO("Client (%s) (fd: %d) connected", ip, client_fd); + + cws_fd_set_nonblocking(client_fd); + cws_epoll_add(epfd, client_fd, EPOLLIN); + + int *key = malloc(sizeof(int)); + *key = client_fd; + struct sockaddr_storage *value = malloc(sizeof(struct sockaddr_storage)); + *value = their_sa; + + mcl_hm_set(clients, key, value); + + return 0; +} + +int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_config *config) { + char data[4096] = {0}; + char ip[INET_ADDRSTRLEN] = {0}; + + /* Incoming data */ + const ssize_t bytes_read = recv(client_fd, data, sizeof(data), 0); + + /* Retrieve client ip */ + int *client_fd_key = malloc(sizeof(int)); + *client_fd_key = client_fd; + mcl_bucket *client = mcl_hm_get(clients, client_fd_key); + free(client_fd_key); + + if (!client) { + CWS_LOG_ERROR("Client fd %d not found in hashmap", client_fd); + cws_epoll_del(epfd, client_fd); + close(client_fd); + + return -1; + } + + struct sockaddr_storage client_sas = *(struct sockaddr_storage *)client->value; + cws_utils_get_client_ip(&client_sas, ip); + + if (bytes_read == 0) { + /* Client disconnected */ + CWS_LOG_INFO("Client (%s) disconnected", ip); + cws_server_close_client(epfd, client_fd, clients); + + return -1; + } + + if (bytes_read < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + /* Error during read, handle it (close client) */ + CWS_LOG_INFO("Client (%s) disconnected (error)", ip); + cws_server_close_client(epfd, client_fd, clients); + } + + return -1; + } + + /* Parse HTTP request */ + cws_http *request = cws_http_parse(data, client_fd, config); + + if (request == NULL) { + CWS_LOG_INFO("Client (%s) disconnected (request NULL)", ip); + cws_server_close_client(epfd, client_fd, clients); + + return -1; + } + + cws_http_send_resource(request); + cws_http_free(request); return 0; } @@ -243,5 +274,9 @@ int cws_server_accept_client(int sockfd, struct sockaddr_storage *their_sa, sock void cws_server_close_client(int epfd, int client_fd, mcl_hashmap *hashmap) { cws_epoll_del(epfd, client_fd); - mcl_hm_remove(hashmap, &client_fd); + + int *key_to_find = malloc(sizeof(int)); + *key_to_find = client_fd; + mcl_hm_remove(hashmap, key_to_find); + free(key_to_find); } diff --git a/src/utils/hashmap.c b/src/utils/hashmap.c deleted file mode 100644 index 8bde341..0000000 --- a/src/utils/hashmap.c +++ /dev/null @@ -1,215 +0,0 @@ -#include "utils/hashmap.h" - -#include -#include -#include - -mcl_hashmap *mcl_hm_init(mcl_hash_fn *hash_fn, mcl_equal_fn *equal_fn, mcl_free_key_fn *free_key_fn, mcl_free_value_fn *free_value_fn) { - /* Allocate memory for hash map struct */ - mcl_hashmap *hashmap = malloc(sizeof(mcl_hashmap)); - - if (hashmap == NULL) { - return NULL; - } - - /* Initialize hash map with given parameters */ - hashmap->hash_fn = hash_fn; - hashmap->equal_fn = equal_fn; - hashmap->free_key_fn = free_key_fn; - hashmap->free_value_fn = free_value_fn; - - /* Clear all buckets in the map */ - memset(hashmap->map, 0, sizeof(hashmap->map)); - - return hashmap; -} - -void mcl_hm_free(mcl_hashmap *hashmap) { - if (hashmap == NULL) { - return; - } - - /* Iterate through all buckets in the hash map */ - for (size_t i = 0; i < MYCLIB_HASHMAP_SIZE; ++i) { - mcl_bucket *bucket = &hashmap->map[i]; - - /* Free the first bucket if it contains data */ - if (bucket->key != NULL) { - if (hashmap->free_key_fn != NULL) { - hashmap->free_key_fn(bucket->key); - } - if (hashmap->free_value_fn != NULL) { - hashmap->free_value_fn(bucket->value); - } - } - - /* Free all chained buckets */ - bucket = bucket->next; - while (bucket != NULL) { - if (hashmap->free_key_fn != NULL) { - hashmap->free_key_fn(bucket->key); - } - if (hashmap->free_value_fn != NULL) { - hashmap->free_value_fn(bucket->value); - } - mcl_bucket *next = bucket->next; - free(bucket); - bucket = next; - } - } - - /* Free the hash map structure itself */ - free(hashmap); -} - -bool mcl_hm_set(mcl_hashmap *hashmap, void *key, void *value) { - /* Validate input parameters */ - if (hashmap == NULL || key == NULL) { - return false; - } - - /* Calculate hash index for the key */ - int index = hashmap->hash_fn(key) % MYCLIB_HASHMAP_SIZE; - mcl_bucket *bucket = &hashmap->map[index]; - - /* If bucket is empty, insert new key-value pair */ - if (bucket->key == NULL) { - bucket->key = key; - bucket->value = value; - bucket->next = NULL; - return true; - } - - /* Check if first bucket has the same key */ - if (hashmap->equal_fn(bucket->key, key)) { - /* Update existing value, free old value if needed */ - if (hashmap->free_value_fn != NULL && bucket->value != NULL) { - hashmap->free_value_fn(bucket->value); - } - bucket->value = value; - return true; - } - - /* Search through the collision chain */ - mcl_bucket *current = bucket->next; - while (current != NULL) { - if (hashmap->equal_fn(current->key, key)) { - /* Update existing value, free old value if needed */ - if (hashmap->free_value_fn != NULL && current->value != NULL) { - hashmap->free_value_fn(current->value); - } - current->value = value; - return true; - } - current = current->next; - } - - /* Key not found, create new bucket and add to chain */ - mcl_bucket *new_bucket = malloc(sizeof(mcl_bucket)); - if (new_bucket == NULL) { - return false; /* Memory allocation failed */ - } - - /* Initialize new bucket and insert at head of chain */ - new_bucket->key = key; - new_bucket->value = value; - new_bucket->next = bucket->next; - bucket->next = new_bucket; - - return true; -} - -mcl_bucket *mcl_hm_get(mcl_hashmap *hashmap, void *key) { - /* Validate input parameters */ - if (hashmap == NULL || key == NULL) { - return NULL; - } - - /* Calculate hash index for the key */ - int index = hashmap->hash_fn(key) % MYCLIB_HASHMAP_SIZE; - mcl_bucket *bucket = &hashmap->map[index]; - - /* Return NULL if bucket is empty */ - if (bucket->key == NULL) { - return NULL; - } - - /* Search through the collision chain */ - while (bucket != NULL) { - if (hashmap->equal_fn(bucket->key, key)) { - return bucket; /* Key found */ - } - bucket = bucket->next; - } - - /* Key not found */ - return NULL; -} - -static void mcl_free_bucket_content(mcl_hashmap *hashmap, mcl_bucket *bucket) { - /* Free key if free function is provided */ - if (hashmap->free_key_fn != NULL && bucket->key != NULL) { - hashmap->free_key_fn(bucket->key); - } - - /* Free value if free function is provided */ - if (hashmap->free_value_fn != NULL && bucket->value != NULL) { - hashmap->free_value_fn(bucket->value); - } -} - -bool mcl_hm_remove(mcl_hashmap *hashmap, void *key) { - /* Validate input parameters */ - if (hashmap == NULL || key == NULL) { - return false; - } - - /* Calculate hash index for the key */ - int index = hashmap->hash_fn(key) % MYCLIB_HASHMAP_SIZE; - mcl_bucket *bucket = &hashmap->map[index]; - - /* Return false if bucket is empty */ - if (bucket->key == NULL) { - return false; - } - - /* Check if first bucket contains the key to remove */ - if (hashmap->equal_fn(bucket->key, key)) { - /* Free the content of the bucket */ - mcl_free_bucket_content(hashmap, bucket); - - if (bucket->next != NULL) { - /* Move next bucket's content to first bucket and free the next bucket */ - mcl_bucket *to_free = bucket->next; - bucket->key = to_free->key; - bucket->value = to_free->value; - bucket->next = to_free->next; - free(to_free); - } else { - /* No next bucket, mark first bucket as empty */ - bucket->key = NULL; - bucket->value = NULL; - bucket->next = NULL; - } - return true; - } - - /* Search through the collision chain */ - mcl_bucket *prev = bucket; - mcl_bucket *current = bucket->next; - - while (current != NULL) { - if (hashmap->equal_fn(current->key, key)) { - /* Key found, free content and unlink bucket */ - mcl_free_bucket_content(hashmap, current); - prev->next = current->next; - free(current); - return true; - } - prev = current; - current = current->next; - } - - /* Key not found */ - return false; -} diff --git a/src/utils/utils.c b/src/utils/utils.c index 5266b7d..163d369 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -97,7 +97,8 @@ bool my_int_equal_fn(const void *a, const void *b) { return false; } -void my_int_free_fn(void *value) { - int fd = *(int *)value; +void my_int_free_key_fn(void *key) { + int fd = *(int *)key; close(fd); + free(key); }