diff --git a/README.md b/README.md index 827dd90..2d99769 100644 --- a/README.md +++ b/README.md @@ -7,5 +7,4 @@ It is not production-ready. Use at your own risk. - Hashmaps - Thread-safe (MT Safe) - Dynamic strings - Thread-safe (MT Safe) - > Requires linking with the C math library (`-lm`) - Circular Queue - Thread-safe (MT Safe) diff --git a/examples/string/str1.c b/examples/string/str1.c index 7f043fa..19a55f1 100644 --- a/examples/string/str1.c +++ b/examples/string/str1.c @@ -1,11 +1,12 @@ #include +#include #include "../../string/mystring.h" int main(void) { size_t length; size_t capacity; - const char *c_str; + char *c_str; /* Allocate a new dynamic string with an initial capacity */ /* Always remember to check return values */ @@ -16,6 +17,7 @@ int main(void) { length = mcl_string_length(str); capacity = mcl_string_capacity(str); printf("%s\nlength: %ld, capacity: %ld\n", c_str, length, capacity); + free(c_str); /* Append text to a mcl_string */ mcl_string_append(str, " How are you?"); @@ -25,6 +27,7 @@ int main(void) { length = mcl_string_length(str); capacity = mcl_string_capacity(str); printf("%s\nsize: %ld, cap: %ld\n", c_str, length, capacity); + free(c_str); /* Always deallocate memory */ mcl_string_free(str); diff --git a/examples/string/str2.c b/examples/string/str2.c new file mode 100644 index 0000000..0ea3411 --- /dev/null +++ b/examples/string/str2.c @@ -0,0 +1,20 @@ +#include +#include + +#include "../../string/mystring.h" + +int main(void) { + mcl_string *str = mcl_string_new("Hello, world!", 0); + mcl_string_append(str, " How are you?"); + + char *c_str = mcl_string_cstr(str); + size_t len = mcl_string_length(str); + size_t cap = mcl_string_capacity(str); + + fprintf(stdout, "%s\nlen: %ld\ncapacity: %ld\n", c_str, len, cap); + + mcl_string_free(str); + free(c_str); + + return 0; +} diff --git a/string/mystring.c b/string/mystring.c index fbc3400..1d6b683 100644 --- a/string/mystring.c +++ b/string/mystring.c @@ -1,11 +1,21 @@ #include "mystring.h" -#include -#include #include #include -mcl_string *mcl_string_new(const char *text, long initial_capacity) { +static size_t mcl_next_power_two(size_t len) { + len--; + len |= len >> 1; + len |= len >> 2; + len |= len >> 4; + len |= len >> 8; + len |= len >> 16; + len++; + + return len; +} + +mcl_string *mcl_string_new(const char *text, size_t initial_capacity) { if (text == NULL) { return NULL; } @@ -16,26 +26,18 @@ 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) { + if (capacity != 0 && capacity - 1 < str->size) { free(str); return NULL; } - if (capacity == -1) { - capacity = (unsigned long)pow(2, (unsigned)log2(str->size) + 1); + if (capacity == 0) { + capacity = mcl_next_power_two(str->size + 1); } str->capacity = capacity; @@ -49,10 +51,18 @@ mcl_string *mcl_string_new(const char *text, long initial_capacity) { } /* Copy the text and ensure null termination */ - memset(str->data, 0, str->capacity); memcpy(str->data, text, str->size); str->data[str->size] = '\0'; + /* Init pthread mutex */ + int ret = pthread_mutex_init(&str->lock, NULL); + if (ret != 0) { + free(str->data); + free(str); + + return NULL; + } + return str; } @@ -79,7 +89,7 @@ int mcl_string_append(mcl_string *string, const char *text) { /* 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); + size_t new_capacity = mcl_next_power_two(new_size + 1); /* Reallocate the buffer */ void *new_data = realloc(string->data, sizeof(char) * new_capacity); if (!new_data) { @@ -114,16 +124,10 @@ 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); @@ -163,9 +167,9 @@ size_t mcl_string_capacity(mcl_string *string) { return cap; } -const char *mcl_string_cstr(mcl_string *string) { +char *mcl_string_cstr(mcl_string *string) { if (string == NULL || string->data == NULL) { - return ""; + return NULL; } int ret = pthread_mutex_lock(&string->lock); @@ -173,7 +177,14 @@ const char *mcl_string_cstr(mcl_string *string) { return NULL; } - char *data = string->data; + char *data = malloc(sizeof(char) * string->size + 1); + if (data == NULL) { + pthread_mutex_unlock(&string->lock); + + return NULL; + } + + memcpy(data, string->data, string->size + 1); pthread_mutex_unlock(&string->lock); diff --git a/string/mystring.h b/string/mystring.h index b13d40b..ca96c6d 100644 --- a/string/mystring.h +++ b/string/mystring.h @@ -5,69 +5,68 @@ #include /** - * @brief Thread-safe dynamic string structure + * @brief Thread-safe dynamic string structure. */ typedef struct mcl_string_t { size_t size; /**< Current length of the string (excluding null terminator) */ - size_t capacity; /**< Allocated capacity */ - char *data; /**< Pointer to string data */ + size_t capacity; /**< Allocated capacity including null terminator */ + char *data; /**< Pointer to string data (null-terminated) */ pthread_mutex_t lock; /**< Mutex for thread safety */ } mcl_string; /** - * @brief Create a new string initialized with given text + * @brief Create a new string initialized with the given text. * - * @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 + * @param text Initial text (must not be NULL) + * @param initial_capacity Initial buffer capacity (0 to auto-calculate) + * @return Pointer to new string on success, or NULL on failure. * - * @note Caller must free the string with mcl_string_free() + * @note Caller must release the string using mcl_string_free(). */ -mcl_string *mcl_string_new(const char *text, long initial_capacity); +mcl_string *mcl_string_new(const char *text, size_t initial_capacity); /** - * @brief Append text to an existing string + * @brief Append text to the string. * - * @param string The string to append to - * @param text Text to append (can be empty) + * @param string String to modify + * @param text Text to append (must not be NULL, can be empty) * @return 0 on success, -1 on failure * - * @note Original string remains unchanged if append fails + * @note On failure, the original string remains unchanged. */ int mcl_string_append(mcl_string *string, const char *text); /** - * @brief Free a string and its resources + * @brief Free the string and its resources. * - * @param string String to free (NULL is safe) + * @param string String to free (safe to call with NULL) */ void mcl_string_free(mcl_string *string); /** - * @brief Get the current length of the string + * @brief Get the current length of the string. * * @param string String to query - * @return Length of string, or 0 if NULL + * @return Length of the string (excluding null terminator), or 0 if NULL */ size_t mcl_string_length(mcl_string *string); /** - * @brief Get the current capacity of the string buffer + * @brief Get the total allocated capacity of the string buffer. * * @param string String to query - * @return Capacity, or 0 if NULL + * @return Capacity (in bytes, including null terminator), or 0 if NULL */ size_t mcl_string_capacity(mcl_string *string); /** - * @brief Get a read-only C-string pointer + * @brief Get a copy of the string as a null-terminated C-string. * - * @param string String to access - * @return Null-terminated string pointer, or "" if NULL + * @param string String to copy + * @return Newly allocated copy of the string, or NULL on failure * - * @warning Do not modify returned pointer. - * Pointer may become invalid after string modifications. + * @warning Caller is responsible for freeing the returned pointer. */ -const char *mcl_string_cstr(mcl_string *string); +char *mcl_string_cstr(mcl_string *string); #endif /* MYCLIB_STRING_H */