From d3c73e4931a664cef10c3ec55ec76c0967698e56 Mon Sep 17 00:00:00 2001 From: Ian Duncan <76043277+dr8co@users.noreply.github.com> Date: Fri, 12 Apr 2024 02:38:33 +0300 Subject: [PATCH] Add new methods --- README.md | 32 ++++++++++ lite_string.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++- lite_string.h | 19 +++++- 3 files changed, 211 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 206022f..aed3333 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ with a focus on simplicity, performance, and ease of use. LiteString is written in C and can be used in both C and C++ projects. +The current implementation does not support Unicode or multibyte characters. + ## Features - Simple and easy to use @@ -129,6 +131,9 @@ A lite_string object is allocated on the heap and must be freed when no longer n lite_string *string_new() // Creates a new string with an initial capacity of 16. +lite_string *string_new_cstr(const char *restrict cstr); +// Creates a new string from a C-string. + void string_free(lite_string *const restrict s) // Frees the memory used by a string. ``` @@ -195,6 +200,9 @@ bool string_insert_range(lite_string *const restrict s, const lite_string *const bool string_erase(lite_string *const restrict s, const size_t index) // Removes the character at a given index in the string. +bool string_erase_range(lite_string *restrict s, size_t start, size_t count) +// Removes a specified number of characters from a string, starting from a specified index. + bool string_push_back(lite_string *const restrict s, const char c) // Appends a character to the end of a string. @@ -227,6 +235,30 @@ bool string_copy(const lite_string *const restrict src, lite_string *const restr bool string_swap(lite_string *const restrict s1, lite_string *const restrict s2) // Swaps the contents of two strings. + +bool string_replace(lite_string *restrict s, const lite_string *restrict old_sub, + const lite_string *restrict new_sub) +// Replaces all occurrences of a substring in a string with another substring. + +void string_replace_char(const lite_string *restrict s, char old_char, char new_char) +// Replaces all occurrences of a character in a string with another character. + +bool string_replace_cstr(lite_string *restrict s, const char *restrict old_cstr, + const char *restrict new_cstr) +// Replaces all occurrences of a C-string in a string with another C-string. +``` + +### Conversion + +```c +void string_to_lower(const lite_string *restrict s); +// Converts a string to lowercase. + +void string_to_upper(const lite_string *restrict s); +// Converts a string to uppercase. + +void string_to_title(const lite_string *restrict s); +// Converts a string to title case. ``` ### Search diff --git a/lite_string.c b/lite_string.c index 2603a41..7fd2058 100644 --- a/lite_string.c +++ b/lite_string.c @@ -244,6 +244,37 @@ char string_front(const lite_string *const restrict s) { return s && s->size ? s->data[0] : '\0'; } +/** + * @brief Erases a range of characters from a string. + * + * @param s A pointer to the string from which the characters will be removed. + * @param start The starting index of the range to be removed. + * @param count The number of characters to be removed. + * @return true if the characters were successfully removed, false otherwise. + */ +bool string_erase_range(lite_string *const restrict s, const size_t start, const size_t count) { + if (s && start < s->size) { + if (count == 0) return true; +#if (__GNUC__ || __clang__) && __has_builtin(__builtin_add_overflow) + size_t end; + if (!__builtin_uaddl_overflow(start, count, &end) && end <= s->size) { +#else + if (count < s->size && start + count <= s->size) { +#endif + // Copy the characters after the range to overwrite the characters to be removed + memmove(s->data + start * sizeof(char), s->data + (start + count) * sizeof(char), + (s->size - start - count) * sizeof(char)); + s->size -= count; + + // Fill the remaining space with null characters + memset(s->data + s->size, '\0', s->capacity - s->size); + return true; + } + } + return false; +} + + /** * @brief Removes the character at a given index in the string. * @@ -447,7 +478,7 @@ string_insert_string(lite_string *const restrict s, const lite_string *const res * @note The returned pointer must be freed by the caller, using \p string_free */ [[nodiscard]] lite_string *string_concat(const lite_string *const restrict s1, - const lite_string *const restrict s2) { + const lite_string *const restrict s2) { if (s1 && s2) { lite_string *s = string_new(); if (s) { @@ -756,7 +787,7 @@ bool string_contains_char(const lite_string *const restrict s, const char c) { * @return The index of the first occurrence of the substring in the string, or \p SIZE_MAX if the substring was not found. */ size_t string_find_from(const lite_string *const restrict s, const lite_string *const restrict sub, - const size_t start) { + const size_t start) { if (s && sub && start < s->size) { if (sub->size == 0) return start; if (sub->size > s->size) return SIZE_MAX; @@ -825,7 +856,7 @@ size_t string_rfind(const lite_string *const restrict s, const lite_string *cons * @return The index of the first occurrence of the C-string in the string, or \p SIZE_MAX if the C-string was not found. */ size_t string_find_cstr_from(const lite_string *const restrict s, const char *const restrict cstr, - const size_t start) { + const size_t start) { // The string and the C-string must be valid if (s && cstr) { const size_t len = strlen(cstr); @@ -1014,3 +1045,131 @@ void string_to_lower(const lite_string *const restrict s) { } } } + +/** + * @brief Converts all the lowercase characters in a string to uppercase. + * + * @param s A pointer to the string to be converted to uppercase. + */ +void string_to_upper(const lite_string *const restrict s) { + if (s) { + for (size_t i = 0; i < s->size; ++i) { + if (s->data[i] >= 'a' && s->data[i] <= 'z') + s->data[i] -= 32; + } + } +} + +/** + * @brief Converts a string to title case. + * + * @param s A pointer to the string to be converted to title case. + */ +void string_to_title(const lite_string *const restrict s) { + if (s) { + if (s->data[0] >= 'a' && s->data[0] <= 'z') + s->data[0] -= 32; + + // Iterate over the rest of the string + for (size_t i = 1; i < s->size; ++i) { + if (s->data[i] == ' ' && s->data[i + 1] >= 'a' && s->data[i + 1] <= 'z') + s->data[i + 1] -= 32; + } + } +} + +/** + * @brief Replaces all occurrences of a substring in a string with another substring. + * + * @param s A pointer to the string where the substrings will be replaced. + * @param old_sub A pointer to the substring to be replaced. + * @param new_sub A pointer to the substring that will replace the old substring. + * @return true if the substrings were successfully replaced, false otherwise. + */ +bool string_replace(lite_string *const restrict s, const lite_string *const restrict old_sub, + const lite_string *const restrict new_sub) { + if (s && old_sub && new_sub) { + if (old_sub->size == 0) return true; + if (old_sub->size > s->size) return false; + + size_t count = 0; + size_t start = 0; + while ((start = string_find_from(s, old_sub, start)) != SIZE_MAX) { + if (string_erase_range(s, start, old_sub->size)) { + if (!string_insert_range(s, new_sub, start, new_sub->size)) return false; + start += new_sub->size; + ++count; + } + } + return count > 0; + } + return false; +} + + +/** + * @brief Replaces all occurrences of a character in a string with another character. + * + * @param s A pointer to the string where the characters will be replaced. + * @param old_char The character to be replaced. + * @param new_char The character to replace the old character. + */ +void string_replace_char(const lite_string *const restrict s, const char old_char, const char new_char) { + if (s && old_char != new_char) { + for (size_t i = 0; i < s->size; ++i) { + if (s->data[i] == old_char) + s->data[i] = new_char; + } + } +} + +/** + * @brief Replaces all occurrences of a C-string in a string with another C-string. + * + * @param s A pointer to the string where the C-strings will be replaced. + * @param old_cstr The C-string to be replaced. + * @param new_cstr The C-string that will replace the old C-string. + * @return true if the C-strings were successfully replaced, false otherwise. + */ +bool string_replace_cstr(lite_string *const restrict s, const char *const restrict old_cstr, + const char *const restrict new_cstr) { + if (s && old_cstr && new_cstr) { + const size_t old_len = strlen(old_cstr); + const size_t new_len = strlen(new_cstr); + if (old_len == 0) return true; + if (old_len > s->size) return false; + + size_t count = 0; + size_t start = 0; + while ((start = string_find_cstr_from(s, old_cstr, start)) != SIZE_MAX) { + if (string_erase_range(s, start, old_len)) { + if (!string_insert_cstr_range(s, new_cstr, start, new_len)) return false; + start += new_len; + ++count; + } + } + return count > 0; + } + return false; +} + +/** + * @brief Duplicates a string. + * + * @param s A pointer to the string to be duplicated. + * @return A pointer to the new string, or NULL if the duplication failed. + * + * @note The returned pointer must be freed by the caller, using the \p string_free() function. + */ +lite_string *string_duplicate(const lite_string *const restrict s) { + if (s) { + lite_string *dup = string_new(); + if (dup) { + if (string_copy(s, dup)) { + return dup; + } + string_free(dup); + } + } + return nullptr; +} diff --git a/lite_string.h b/lite_string.h index 4215476..5a3d2a2 100644 --- a/lite_string.h +++ b/lite_string.h @@ -52,8 +52,8 @@ extern "C" { * When the size reaches the capacity, the string is resized to a larger capacity to accommodate more characters. */ struct lite_string { - char *data; ///< A pointer to the character data. - size_t size; ///< The number of characters in the string, not including the null character. + char *data; ///< A pointer to the character data. + size_t size; ///< The number of characters in the string, not including the null character. size_t capacity; ///< The total number of characters that the string can hold. }; @@ -168,8 +168,23 @@ bool string_shrink_to_fit(lite_string *restrict s); void string_to_lower(const lite_string *restrict s); +void string_to_upper(const lite_string *restrict s); + +void string_to_title(const lite_string *restrict s); + [[nodiscard]] lite_string *string_new_cstr(const char *restrict cstr); +bool string_replace(lite_string *restrict s, const lite_string *restrict old_sub, + const lite_string *restrict new_sub); + +void string_replace_char(const lite_string *restrict s, char old_char, char new_char); + +bool string_replace_cstr(lite_string *restrict s, const char *restrict old_cstr, + const char *restrict new_cstr); + +bool string_erase_range(lite_string *restrict s, size_t start, size_t count); + +lite_string *string_duplicate(const lite_string *restrict s); #if __cplusplus } #endif