diff --git a/include/stringzilla/stringzilla.h b/include/stringzilla/stringzilla.h index 9ec86112..6d62a7b3 100644 --- a/include/stringzilla/stringzilla.h +++ b/include/stringzilla/stringzilla.h @@ -651,11 +651,12 @@ SZ_PUBLIC sz_size_t sz_string_erase(sz_string_t *string, sz_size_t offset, sz_si /** * @brief Shrinks the string to fit the current length, if it's allocated on the heap. - * Teh reverse operation of ::sz_string_reserve. + * It's the reverse operation of ::sz_string_reserve. * * @param string String to shrink. * @param allocator Memory allocator to use for the allocation. * @return Whether the operation was successful. The only failures can come from the allocator. + * On failure, the string will remain unchanged. */ SZ_PUBLIC sz_ptr_t sz_string_shrink_to_fit(sz_string_t *string, sz_memory_allocator_t *allocator); @@ -3217,7 +3218,7 @@ SZ_PUBLIC sz_ptr_t sz_string_init_length(sz_string_t *string, sz_size_t length, SZ_PUBLIC sz_ptr_t sz_string_reserve(sz_string_t *string, sz_size_t new_capacity, sz_memory_allocator_t *allocator) { - sz_assert(string && "String can't be SZ_NULL."); + sz_assert(string && allocator && "Strings and allocators can't be SZ_NULL."); sz_size_t new_space = new_capacity + 1; if (new_space <= SZ_STRING_INTERNAL_SPACE) return string->external.start; @@ -3243,6 +3244,34 @@ SZ_PUBLIC sz_ptr_t sz_string_reserve(sz_string_t *string, sz_size_t new_capacity return string->external.start; } +SZ_PUBLIC sz_ptr_t sz_string_shrink_to_fit(sz_string_t *string, sz_memory_allocator_t *allocator) { + + sz_assert(string && allocator && "Strings and allocators can't be SZ_NULL."); + + sz_ptr_t string_start; + sz_size_t string_length; + sz_size_t string_space; + sz_bool_t string_is_external; + sz_string_unpack(string, &string_start, &string_length, &string_space, &string_is_external); + + // We may already be space-optimal, and in that case we don't need to do anything. + sz_size_t new_space = string_length + 1; + if (string_space == new_space || string_is_external) return string->external.start; + + sz_ptr_t new_start = (sz_ptr_t)allocator->allocate(new_space, allocator->handle); + if (!new_start) return SZ_NULL_CHAR; + + sz_copy(new_start, string_start, string_length); + string->external.start = new_start; + string->external.space = new_space; + string->external.padding = 0; + string->external.length = string_length; + + // Deallocate the old string. + if (string_is_external) allocator->free(string_start, string_space, allocator->handle); + return string->external.start; +} + SZ_PUBLIC sz_ptr_t sz_string_expand(sz_string_t *string, sz_size_t offset, sz_size_t added_length, sz_memory_allocator_t *allocator) { @@ -3333,7 +3362,7 @@ SZ_PUBLIC void sz_fill_serial(sz_ptr_t target, sz_size_t length, sz_u8_t value) // In case of long strings, skip unaligned bytes, and then fill the rest in 64-bit chunks. else { - sz_u64_t value64 = (sz_u64_t)(value) * 0x0101010101010101ull; + sz_u64_t value64 = (sz_u64_t)value * 0x0101010101010101ull; while ((sz_size_t)target & 7ull) *(target++) = value; while (target + 8 <= end) *(sz_u64_t *)target = value64, target += 8; while (target != end) *(target++) = value; diff --git a/include/stringzilla/stringzilla.hpp b/include/stringzilla/stringzilla.hpp index e64ebc44..7f4a5c21 100644 --- a/include/stringzilla/stringzilla.hpp +++ b/include/stringzilla/stringzilla.hpp @@ -2604,23 +2604,77 @@ class basic_string { #pragma region Modifiers #pragma region Non-STL API + /** + * @brief Resizes the string to a specified number of characters, padding with the specified character if needed. + * @param count The new size of the string. + * @param character The character to fill new elements with, if expanding. Defaults to null character. + * @return `true` if the resizing was successful, `false` otherwise. + */ bool try_resize(size_type count, value_type character = '\0') noexcept; + /** + * @brief Attempts to reduce memory usage by freeing unused memory. + * @return `true` if the operation was successful and potentially reduced the memory footprint, `false` otherwise. + */ + bool try_shrink_to_fit() noexcept { + return _with_alloc([&](sz_alloc_type &alloc) { return sz_string_shrink_to_fit(&string_, &alloc); }); + } + + /** + * @brief Attempts to reserve enough space for a specified number of characters. + * @param capacity The new capacity to reserve. + * @return `true` if the reservation was successful, `false` otherwise. + */ bool try_reserve(size_type capacity) noexcept { return _with_alloc([&](sz_alloc_type &alloc) { return sz_string_reserve(&string_, capacity, &alloc); }); } + /** + * @brief Assigns a new value to the string, replacing its current contents. + * @param other The string view whose contents to assign. + * @return `true` if the assignment was successful, `false` otherwise. + */ bool try_assign(string_view other) noexcept; + /** + * @brief Assigns a concatenated sequence to the string, replacing its current contents. + * @param other The concatenation object representing the sequence to assign. + * @return `true` if the assignment was successful, `false` otherwise. + */ template bool try_assign(concatenation const &other) noexcept; + /** + * @brief Attempts to add a single character to the end of the string. + * @param c The character to add. + * @return `true` if the character was successfully added, `false` otherwise. + */ bool try_push_back(char_type c) noexcept; + /** + * @brief Attempts to append a given character array to the string. + * @param str The pointer to the array of characters to append. + * @param length The number of characters to append. + * @return `true` if the append operation was successful, `false` otherwise. + */ bool try_append(const_pointer str, size_type length) noexcept; + /** + * @brief Attempts to append a string view to the string. + * @param str The string view to append. + * @return `true` if the append operation was successful, `false` otherwise. + */ bool try_append(string_view str) noexcept { return try_append(str.data(), str.size()); } + /** + * @brief Clears the contents of the string and resets its length to 0. + * @return Always returns `true` as this operation cannot fail under normal conditions. + */ + bool try_clear() noexcept { + clear(); + return true; + } + /** * @brief Erases ( @b in-place ) a range of characters defined with signed offsets. * @return Number of characters removed. @@ -2683,6 +2737,14 @@ class basic_string { if (!try_resize(count, character)) throw std::bad_alloc(); } + /** + * @brief Reclaims the unused memory, if any. + * @throw `std::bad_alloc` if the allocation fails. + */ + void shrink_to_fit() noexcept(false) { + if (!try_shrink_to_fit()) throw std::bad_alloc(); + } + /** * @brief Informs the string object of a planned change in size, so that it pre-allocate once. * @throw `std::length_error` if the string is too long.