diff --git a/README.md b/README.md index 7824074..08fc1a9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,6 @@ It is not production-ready. Use at your own risk. ## Features - Hashmaps - Thread-safe (MT Safe) -- Dynamic strings - Not thread-safe (MT Unsafe) +- Dynamic strings - Thread-safe (MT Safe) > Requires linking with the C math library (`-lm`) - Circular Queue - Not thread-safe (MT Unsafe) diff --git a/string/mystring.c b/string/mystring.c index 1d95df7..fbc3400 100644 --- a/string/mystring.c +++ b/string/mystring.c @@ -1,6 +1,7 @@ #include "mystring.h" #include +#include #include #include @@ -15,11 +16,21 @@ mcl_string *mcl_string_new(const char *text, long initial_capacity) { return NULL; } + /* Init pthread mutex */ + int ret = pthread_mutex_init(&str->lock, NULL); + if (ret != 0) { + free(str); + + return NULL; + } + /* Calculate size and capacity */ str->size = strlen(text); size_t capacity = initial_capacity; if (capacity != -1 && capacity - 1 < str->size) { + free(str); + return NULL; } @@ -50,9 +61,17 @@ int mcl_string_append(mcl_string *string, const char *text) { return -1; } + /* Lock resource */ + int ret = pthread_mutex_lock(&string->lock); + if (ret != 0) { + return -1; + } + /* Handle empty case */ size_t text_len = strlen(text); if (text_len == 0) { + pthread_mutex_unlock(&string->lock); + return 0; } @@ -64,6 +83,8 @@ int mcl_string_append(mcl_string *string, const char *text) { /* Reallocate the buffer */ void *new_data = realloc(string->data, sizeof(char) * new_capacity); if (!new_data) { + pthread_mutex_unlock(&string->lock); + return -1; } @@ -79,6 +100,12 @@ int mcl_string_append(mcl_string *string, const char *text) { string->size = new_size; string->data[string->size] = '\0'; + /* Unlock resource */ + ret = pthread_mutex_unlock(&string->lock); + if (ret != 0) { + return -1; + } + return 0; } @@ -87,33 +114,68 @@ void mcl_string_free(mcl_string *string) { return; } + int ret = pthread_mutex_lock(&string->lock); + if (ret != 0) { + return; + } + if (string->data) { free(string->data); } + pthread_mutex_unlock(&string->lock); + pthread_mutex_destroy(&string->lock); + free(string); } -size_t mcl_string_length(const mcl_string *string) { +size_t mcl_string_length(mcl_string *string) { if (string == NULL) { return 0; } - return string->size; + int ret = pthread_mutex_lock(&string->lock); + if (ret != 0) { + return 0; + } + + size_t len = string->size; + + pthread_mutex_unlock(&string->lock); + + return len; } -size_t mcl_string_capacity(const mcl_string *string) { +size_t mcl_string_capacity(mcl_string *string) { if (string == NULL) { return 0; } - return string->capacity; + int ret = pthread_mutex_lock(&string->lock); + if (ret != 0) { + return 0; + } + + size_t cap = string->capacity; + + pthread_mutex_unlock(&string->lock); + + return cap; } -const char *mcl_string_cstr(const mcl_string *string) { +const char *mcl_string_cstr(mcl_string *string) { if (string == NULL || string->data == NULL) { return ""; } - return string->data; + int ret = pthread_mutex_lock(&string->lock); + if (ret != 0) { + return NULL; + } + + char *data = string->data; + + pthread_mutex_unlock(&string->lock); + + return data; } diff --git a/string/mystring.h b/string/mystring.h index f6daa54..b13d40b 100644 --- a/string/mystring.h +++ b/string/mystring.h @@ -1,25 +1,27 @@ #ifndef MYCLIB_STRING_H #define MYCLIB_STRING_H +#include #include /** - * @brief String structure + * @brief Thread-safe dynamic 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 */ + size_t size; /**< Current length of the string (excluding null terminator) */ + size_t capacity; /**< Allocated capacity */ + char *data; /**< Pointer to string data */ + pthread_mutex_t lock; /**< Mutex for thread safety */ } mcl_string; /** - * @brief Create a new string + * @brief Create a new string initialized with given text * - * @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 + * @param text Initial text (cannot be NULL) + * @param initial_capacity Initial buffer capacity; pass -1 to auto-calculate + * @return Pointer to 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 + * @note Caller must free the string with mcl_string_free() */ mcl_string *mcl_string_new(const char *text, long initial_capacity); @@ -27,47 +29,45 @@ 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) + * @param text Text to append (can be empty) * @return 0 on success, -1 on failure * - * @note If it fails, the original string remains unchanged + * @note Original string remains unchanged if append fails */ int mcl_string_append(mcl_string *string, const char *text); /** - * @brief Free a string + * @brief Free a string and its resources * - * @param string The string to free - * - * @note This function is safe to call with NULL pointers + * @param string String to free (NULL is safe) */ 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 + * @param string String to query + * @return Length of string, or 0 if NULL */ -size_t mcl_string_length(const mcl_string *string); +size_t mcl_string_length(mcl_string *string); /** - * @brief Get the current capacity of the string + * @brief Get the current capacity of the string buffer * - * @param string The string to query - * @return The capacity of the string buffer, or 0 if string is NULL + * @param string String to query + * @return Capacity, or 0 if NULL */ -size_t mcl_string_capacity(const mcl_string *string); +size_t mcl_string_capacity(mcl_string *string); /** - * @brief Get a read-only string representation + * @brief Get a read-only C-string pointer * - * @param string The string to access - * @return Pointer to null-terminated string data, or empty string "" if string is NULL + * @param string String to access + * @return Null-terminated string pointer, or "" if NULL * - * @warning The returned pointer should not be modified directly and may become - * invalid after any modification operation on the string + * @warning Do not modify returned pointer. + * Pointer may become invalid after string modifications. */ -const char *mcl_string_cstr(const mcl_string *string); +const char *mcl_string_cstr(mcl_string *string); #endif /* MYCLIB_STRING_H */