Fix guf_str bugs and add tests

This commit is contained in:
jun 2025-04-23 21:12:24 +02:00
parent 0910ee4bd8
commit e98dc3b91e
4 changed files with 463 additions and 95 deletions

View File

@ -84,10 +84,9 @@ GUF_STR_KWRDS guf_str_view guf_str_view_trim_left_ascii(guf_str_view sv);
GUF_STR_KWRDS guf_str_view guf_str_next_tok(guf_str_view *input, const guf_str_view *delims, ptrdiff_t num_delims, const guf_str_view *preserved_delims, ptrdiff_t num_preserved_delims); GUF_STR_KWRDS guf_str_view guf_str_next_tok(guf_str_view *input, const guf_str_view *delims, ptrdiff_t num_delims, const guf_str_view *preserved_delims, ptrdiff_t num_preserved_delims);
// guf_str: // guf_str:
// DONE:
GUF_STR_KWRDS guf_str *guf_str_try_init(guf_str *str, guf_str_view str_view, guf_allocator *alloc, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_init(guf_str *str, guf_str_view str_view, guf_allocator *alloc, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_init(guf_str *str, guf_str_view str_view, guf_allocator *alloc); GUF_STR_KWRDS guf_str *guf_str_init(guf_str *str, guf_str_view str_view, guf_allocator *alloc);
GUF_STR_KWRDS guf_str *guf_str_init_empty(guf_str *str, guf_allocator *alloc);
GUF_STR_KWRDS guf_str *guf_str_try_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc); GUF_STR_KWRDS guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc);
@ -98,11 +97,24 @@ GUF_STR_KWRDS guf_str guf_str_new(guf_str_view str_view, guf_allocator *alloc);
GUF_STR_KWRDS void guf_str_free(guf_str *str, void *ctx); GUF_STR_KWRDS void guf_str_free(guf_str *str, void *ctx);
GUF_STR_KWRDS guf_str *guf_str_copy(guf_str *dst, const guf_str *src, void *ctx); GUF_STR_KWRDS guf_str *guf_str_copy(guf_str *dst, const guf_str *src, void *ctx);
GUF_STR_KWRDS guf_str *guf_str_move(guf_str *dst, guf_str *src, void *ctx); GUF_STR_KWRDS guf_str *guf_str_move(guf_str *dst, guf_str *src, void *ctx);
GUF_STR_KWRDS bool guf_str_equal(const guf_str *a, const guf_str *b); GUF_STR_KWRDS bool guf_str_equal(const guf_str *a, const guf_str *b);
GUF_STR_KWRDS int guf_str_cmp(const guf_str *a, const guf_str *b); GUF_STR_KWRDS int guf_str_cmp(const guf_str *a, const guf_str *b);
// DONE: // Reserve at least min_capacity characters (excluding the null-terminator) (try to double the current capacity first; if that's not at least min_capacity, set the new capacity to min_capacity instead).
GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t min_capacity, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_reserve(guf_str *str, ptrdiff_t min_capacity);
// Shrink the capacity of the string so it does not waste space (short-string-optimisation will be applied if the new capacity <= GUF_STR_SSO_BUF_CAP)
GUF_STR_KWRDS guf_str *guf_str_try_shrink_to_fit(guf_str *str, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_shrink_to_fit(guf_str *str);
// Set the contents of str to the given string view (mutating str) -> return the mutated str
GUF_STR_KWRDS guf_str *guf_str_try_set(guf_str *str, guf_str_view str_view, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_set(guf_str *str, guf_str_view str_view);
// Return a view of the string.
GUF_STR_KWRDS guf_str_view guf_str_to_view(const guf_str *str);
// Return a non-const pointer to the character at the specified index of str (if possible)
GUF_STR_KWRDS char *guf_str_try_at(guf_str *str, ptrdiff_t idx, guf_err *err); GUF_STR_KWRDS char *guf_str_try_at(guf_str *str, ptrdiff_t idx, guf_err *err);
GUF_STR_KWRDS char *guf_str_at(guf_str *str, ptrdiff_t idx); GUF_STR_KWRDS char *guf_str_at(guf_str *str, ptrdiff_t idx);
GUF_STR_KWRDS char *guf_str_try_back(guf_str *str, guf_err *err); GUF_STR_KWRDS char *guf_str_try_back(guf_str *str, guf_err *err);
@ -110,6 +122,7 @@ GUF_STR_KWRDS char *guf_str_back(guf_str *str);
GUF_STR_KWRDS char *guf_str_try_front(guf_str *str, guf_err *err); GUF_STR_KWRDS char *guf_str_try_front(guf_str *str, guf_err *err);
GUF_STR_KWRDS char *guf_str_front(guf_str *str); GUF_STR_KWRDS char *guf_str_front(guf_str *str);
// Return a copy of the char at the specified index of str (if possible)
GUF_STR_KWRDS char guf_str_try_at_cpy(const guf_str *str, ptrdiff_t idx, guf_err *err); GUF_STR_KWRDS char guf_str_try_at_cpy(const guf_str *str, ptrdiff_t idx, guf_err *err);
GUF_STR_KWRDS char guf_str_at_cpy(const guf_str *str, ptrdiff_t idx); GUF_STR_KWRDS char guf_str_at_cpy(const guf_str *str, ptrdiff_t idx);
GUF_STR_KWRDS char guf_str_try_back_cpy(const guf_str *str, guf_err *err); GUF_STR_KWRDS char guf_str_try_back_cpy(const guf_str *str, guf_err *err);
@ -117,45 +130,42 @@ GUF_STR_KWRDS char guf_str_back_cpy(const guf_str *str);
GUF_STR_KWRDS char guf_str_try_front_cpy(const guf_str *str, guf_err *err); GUF_STR_KWRDS char guf_str_try_front_cpy(const guf_str *str, guf_err *err);
GUF_STR_KWRDS char guf_str_front_cpy(const guf_str *str); GUF_STR_KWRDS char guf_str_front_cpy(const guf_str *str);
// DONE: /*
// Make substring in-place (constant time if pos == 0, otherwise copying count chars to the beginning of the str, i.e. linear time) Turn str into the substring in range [pos, pos + count) (mutating str) -> return the mutated str
(Constant time if pos == 0, otherwise copying count chars to the beginning of the str, i.e. linear time.)
NOTE: To make a substring-copy (instead of mutating str), create a guf_str_view of str and use guf_str_view_substr
*/
GUF_STR_KWRDS guf_str *guf_str_try_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count); GUF_STR_KWRDS guf_str *guf_str_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count);
// TODO: // Remove the last character from str if possible (mutating str) -> return the popped char
GUF_STR_KWRDS char guf_str_try_pop_back(guf_str *str, guf_err *err);
GUF_STR_KWRDS char guf_str_pop_back(guf_str *str); GUF_STR_KWRDS char guf_str_pop_back(guf_str *str);
GUF_STR_KWRDS char guf_str_pop_front(guf_str *str);
// DONE: // Append a char to str (n times; times must be >= 0) (mutating str) -> return the mutated str
GUF_STR_KWRDS guf_str *guf_str_try_append_char(guf_str *str, char c, ptrdiff_t times, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_append_char(guf_str *str, char c, ptrdiff_t times, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_append_char(guf_str *str, char c, ptrdiff_t times); GUF_STR_KWRDS guf_str *guf_str_append_char(guf_str *str, char c, ptrdiff_t times);
GUF_STR_KWRDS guf_str *guf_str_try_append_one_char(guf_str *str, char c, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_append_one_char(guf_str *str, char c, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_append_one_char(guf_str *str, char c); GUF_STR_KWRDS guf_str *guf_str_append_one_char(guf_str *str, char c);
// Append str_view to str (mutating str) -> return the mutated str
GUF_STR_KWRDS guf_str *guf_str_try_append(guf_str *str, guf_str_view sv, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_append(guf_str *str, guf_str_view sv, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_append(guf_str *str, guf_str_view sv); GUF_STR_KWRDS guf_str *guf_str_append(guf_str *str, guf_str_view sv);
GUF_STR_KWRDS guf_str *guf_str_try_append_cstr(guf_str *str, const char *c_str, guf_err *err); // Return a pointer to the null-terminated char array representing the string (works like std::string::c_str in C++)
GUF_STR_KWRDS guf_str *guf_str_append_cstr(guf_str *str, const char *c_str);
// DONE:
GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t min_capacity, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_reserve(guf_str *str, ptrdiff_t min_capacity);
// TODO:
GUF_STR_KWRDS guf_str *guf_str_try_shrink_to_fit(guf_str *str, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_shrink_to_fit(guf_str *str);
// DONE:
GUF_STR_KWRDS const char *guf_str_const_cstr(const guf_str *str); GUF_STR_KWRDS const char *guf_str_const_cstr(const guf_str *str);
GUF_STR_KWRDS char *guf_str_try_get_cstr(guf_str *str, guf_err *err); // Error if str is readonly. GUF_STR_KWRDS char *guf_str_try_get_cstr(guf_str *str, guf_err *err); // Error if str is readonly.
GUF_STR_KWRDS char *guf_str_cstr(guf_str *str); // Panics if str is readonly. GUF_STR_KWRDS char *guf_str_cstr(guf_str *str); // Panics if str is readonly.
GUF_STR_KWRDS ptrdiff_t guf_str_len(const guf_str *str); // The length (in chars) without the final zero-terminator. // Return the length/capacity (in chars) *without* the final null-terminator.
GUF_STR_KWRDS ptrdiff_t guf_str_capacity(const guf_str *str); // The capacity (in chars) without the final zero-terminator. GUF_STR_KWRDS ptrdiff_t guf_str_len(const guf_str *str);
GUF_STR_KWRDS ptrdiff_t guf_str_capacity(const guf_str *str);
// Return true if the char data of the string lives directly within the guf_str itself (short-string optimisation) instead of in a separate dynamic allocation
GUF_STR_KWRDS bool guf_str_is_short(const guf_str *str); GUF_STR_KWRDS bool guf_str_is_short(const guf_str *str);
// Return true if the string is in readonly ("view") mode, i.e. can't be modified, copied etc. which is useful for guf_dict so we don't have to use guf_str_view but can use guf_str (by passing a read-only guf_str) for the lookup functions.
GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str); GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str);
// Return true if the string's data does not violate its invariants (useful for debugging, should never be false after initialising if there are not bugs in guf_str).
GUF_STR_KWRDS bool guf_str_is_valid(const guf_str *str); GUF_STR_KWRDS bool guf_str_is_valid(const guf_str *str);
GUF_STR_KWRDS guf_str guf_str_new_uninitialised(void); GUF_STR_KWRDS guf_str guf_str_new_uninitialised(void);
@ -198,7 +208,7 @@ GUF_STR_KWRDS bool guf_str_is_uninit(const guf_str *str);
} }
static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size_with_null) static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size_with_null)
{ {
GUF_ASSERT(size_with_null < GUF_STR_SSO_BUF_CAP && size_with_null < 0x80); GUF_ASSERT(size_with_null <= GUF_STR_SSO_BUF_CAP && size_with_null < 0x80); // TODO: was < SSO_CAP, should be <= SSO_CAP?
str->data.shrt.size = (unsigned char)(size_with_null << 1); str->data.shrt.size = (unsigned char)(size_with_null << 1);
} }
#elif defined(GUF_PLATFORM_BIG_ENDIAN) #elif defined(GUF_PLATFORM_BIG_ENDIAN)
@ -214,7 +224,7 @@ GUF_STR_KWRDS bool guf_str_is_uninit(const guf_str *str);
} }
static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size_with_null) static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size_with_null)
{ {
GUF_ASSERT(size_with_null < GUF_STR_SSO_BUF_CAP && size_with_null < 0x80); GUF_ASSERT(size_with_null <= GUF_STR_SSO_BUF_CAP && size_with_null < 0x80); // TODO: was < SSO_CAP, should be <=
str->data.shrt.size = size_with_null; str->data.shrt.size = size_with_null;
} }
#else #else
@ -328,20 +338,25 @@ GUF_STR_KWRDS bool guf_str_is_valid(const guf_str *str)
return size > 0 && size <= GUF_STR_SSO_BUF_CAP && str->data.shrt.c_str[size - 1] == '\0'; return size > 0 && size <= GUF_STR_SSO_BUF_CAP && str->data.shrt.c_str[size - 1] == '\0';
} else { } else {
const size_t cap_with_null = guf_str_cap_internal_(str) + 1; const size_t cap_with_null = guf_str_cap_internal_(str) + 1;
const size_t size = guf_str_size_internal_(str); // len + 1
const bool valid_cap = cap_with_null > GUF_STR_SSO_BUF_CAP && cap_with_null <= PTRDIFF_MAX && (cap_with_null % 2 == 0); const bool valid_cap = cap_with_null > GUF_STR_SSO_BUF_CAP && cap_with_null <= PTRDIFF_MAX && (cap_with_null % 2 == 0);
return valid_cap && str->data.lng.c_str && str->data.lng.size > 0 && str->data.lng.size <= cap_with_null; return valid_cap && size >= 1 && str->data.lng.c_str && str->data.lng.size > 0 && str->data.lng.size <= cap_with_null && str->data.lng.c_str[size - 1] == '\0';
} }
} }
GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t new_cap_min, guf_err *err) GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t new_cap_min, guf_err *err)
{ {
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
GUF_ASSERT(!guf_str_is_readonly(str));
if (guf_str_is_readonly(str)) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in guf_str_try_reserve: guf_str is readonly"));
return NULL;
}
const size_t old_cap_with_null = guf_str_cap_internal_(str) + 1; const size_t old_cap_with_null = guf_str_cap_internal_(str) + 1;
const size_t len_with_null = guf_str_len_internal_(str) + 1; const size_t len_with_null = guf_str_len_internal_(str) + 1;
if (new_cap_min <= (ptrdiff_t)old_cap_with_null) { // No need to grow. if (new_cap_min < (ptrdiff_t)old_cap_with_null) { // No need to grow. TODO: was <=, should be < ?
guf_err_set_if_not_null(err, GUF_ERR_NONE); guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str; return str;
} }
@ -382,6 +397,7 @@ GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t new_cap_min,
} }
memcpy(c_str_new, str->data.shrt.c_str, len_with_null); memcpy(c_str_new, str->data.shrt.c_str, len_with_null);
str->data.lng.c_str = c_str_new; str->data.lng.c_str = c_str_new;
str->data.lng.size = len_with_null;
guf_str_set_lng_cap_(str, new_cap_min_with_null); guf_str_set_lng_cap_(str, new_cap_min_with_null);
} else { // b) Was long string -> need re-allocation } else { // b) Was long string -> need re-allocation
char *c_str_new = str->allocator->realloc(str->data.lng.c_str, old_cap_with_null, new_cap_min_with_null, str->allocator->ctx); char *c_str_new = str->allocator->realloc(str->data.lng.c_str, old_cap_with_null, new_cap_min_with_null, str->allocator->ctx);
@ -398,9 +414,67 @@ GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t new_cap_min,
return str; return str;
} }
GUF_STR_KWRDS guf_str *guf_str_reserve(guf_str *str, ptrdiff_t new_cap_min)
{
return guf_str_try_reserve(str, new_cap_min, NULL);
}
GUF_STR_KWRDS guf_str *guf_str_try_shrink_to_fit(guf_str *str, guf_err *err)
{
GUF_ASSERT(guf_str_is_valid(str));
if (guf_str_is_readonly(str)) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in guf_str_try_shrink_to_fit: guf_str is readonly"));
return NULL;
}
const size_t old_cap_with_null = guf_str_cap_internal_(str) + 1;
const size_t len_with_null = guf_str_len_internal_(str) + 1;
GUF_ASSERT(len_with_null <= old_cap_with_null);
if (old_cap_with_null == len_with_null || guf_str_is_short_internal_(str)) {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
char *c_str_old = guf_str_cstr(str);
GUF_ASSERT(c_str_old);
if (len_with_null <= GUF_STR_SSO_BUF_CAP) { // a) Shrunk size fits into short.string.
GUF_ASSERT(len_with_null <= UCHAR_MAX)
guf_str_set_shrt_size_(str, (unsigned char)len_with_null);
memcpy(str->data.shrt.c_str, c_str_old, len_with_null);
str->allocator->free(c_str_old, old_cap_with_null, str->allocator->ctx);
GUF_ASSERT(guf_str_is_short(str));
GUF_ASSERT(guf_str_is_valid(str));
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
// b) Shrunk size does not fit into short-string.
char *c_str_new = str->allocator->realloc(c_str_old, old_cap_with_null, len_with_null, str->allocator->ctx);
if (!c_str_new) {
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in guf_str_try_shrink_to_fit: realloc failed"));
return NULL;
} else {
str->data.lng.c_str = c_str_new;
guf_str_set_lng_cap_(str, len_with_null);
GUF_ASSERT(!guf_str_is_short(str));
GUF_ASSERT(guf_str_is_valid(str));
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
}
GUF_STR_KWRDS guf_str *guf_str_shrink_to_fit(guf_str *str)
{
return guf_str_try_shrink_to_fit(str, NULL);
}
static char *guf_str_get_cstr_internal_(guf_str *str) static char *guf_str_get_cstr_internal_(guf_str *str)
{ {
if (guf_str_is_short(str)) { if (guf_str_is_short_internal_(str)) {
return str->data.shrt.c_str; return str->data.shrt.c_str;
} else { } else {
return str->data.lng.c_str; return str->data.lng.c_str;
@ -470,14 +544,15 @@ GUF_STR_KWRDS bool guf_str_is_uninit(const guf_str *str)
GUF_STR_KWRDS guf_str *guf_str_init_empty(guf_str *str, guf_allocator *allocator) GUF_STR_KWRDS guf_str *guf_str_init_empty(guf_str *str, guf_allocator *allocator)
{ {
GUF_ASSERT(str && allocator); GUF_ASSERT_RELEASE(str && allocator);
GUF_ASSERT_RELEASE(allocator->alloc && allocator->realloc && allocator->free);
str->allocator = allocator; str->allocator = allocator;
guf_str_set_shrt_size_(str, 1); guf_str_set_shrt_size_(str, 1);
str->data.shrt.c_str[0] = '\0'; str->data.shrt.c_str[0] = '\0';
GUF_ASSERT(guf_str_is_valid(str));
return str; return str;
} }
GUF_STR_KWRDS guf_str *guf_str_try_init(guf_str *str, guf_str_view str_view, guf_allocator *alloc, guf_err *err) GUF_STR_KWRDS guf_str *guf_str_try_init(guf_str *str, guf_str_view str_view, guf_allocator *alloc, guf_err *err)
{ {
if (!str) { if (!str) {
@ -498,11 +573,14 @@ GUF_STR_KWRDS guf_str *guf_str_try_init(guf_str *str, guf_str_view str_view, guf
if (str_view.len == 0) { if (str_view.len == 0) {
GUF_ASSERT(!guf_str_is_readonly(str)); GUF_ASSERT(!guf_str_is_readonly(str));
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str; return str;
} }
GUF_ASSERT(str_view.str && str_view.len > 0); GUF_ASSERT(str_view.str && str_view.len > 0);
guf_str_try_reserve(str, str_view.len, err); guf_str_try_reserve(str, str_view.len, err);
GUF_ASSERT(guf_str_is_valid(str));
if (err && *err != GUF_ERR_NONE) { if (err && *err != GUF_ERR_NONE) {
guf_panic(*err, GUF_ERR_MSG("in guf_str_try_init: Initial allocation failed")); guf_panic(*err, GUF_ERR_MSG("in guf_str_try_init: Initial allocation failed"));
return NULL; return NULL;
@ -512,9 +590,10 @@ GUF_STR_KWRDS guf_str *guf_str_try_init(guf_str *str, guf_str_view str_view, guf
GUF_ASSERT(!guf_str_is_readonly(str)); GUF_ASSERT(!guf_str_is_readonly(str));
char *c_str_dst = guf_str_get_cstr_internal_(str); char *c_str_dst = guf_str_get_cstr_internal_(str);
GUF_ASSERT_RELEASE(c_str_dst); GUF_ASSERT(c_str_dst);
memcpy(c_str_dst, str_view.str, str_view.len); memcpy(c_str_dst, str_view.str, str_view.len);
c_str_dst[str_view.len] = '\0'; c_str_dst[str_view.len] = '\0';
guf_str_set_len_internal_(str, str_view.len);
GUF_ASSERT(!guf_str_is_readonly(str)); GUF_ASSERT(!guf_str_is_readonly(str));
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
@ -536,13 +615,65 @@ GUF_STR_KWRDS guf_str guf_str_try_new(guf_str_view str_view, guf_allocator *allo
return guf_str_new_uninitialised(); return guf_str_new_uninitialised();
} else { } else {
GUF_ASSERT(!guf_str_is_uninit(&str)); GUF_ASSERT(!guf_str_is_uninit(&str));
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str; return str;
} }
} }
GUF_STR_KWRDS guf_str guf_str_new(guf_str_view str_view, guf_allocator *alloc)
{
return guf_str_try_new(str_view, alloc, NULL);
}
GUF_STR_KWRDS guf_str *guf_str_try_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc, guf_err *err)
{
GUF_ASSERT(str);
if (!str) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, GUF_ERR_MSG("in guf_str_try_init_from_cstr: str is NULL"));
return NULL;
} else if (!c_str) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, GUF_ERR_MSG("in guf_str_try_init_from_cstr: c_str is NULL"));
return NULL;
} else if (!alloc || !alloc->alloc || !alloc->realloc || !alloc->free) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, GUF_ERR_MSG("in guf_str_try_init_from_cstr: alloc (or allocs function pointers) is/are NULL"));
return NULL;
}
const size_t len = strlen(c_str);
if (len >= PTRDIFF_MAX) {
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in guf_str_try_init_from_cstr: stlen(c_str) >= PTRDIFF_MAX"));
return NULL;
}
guf_str_try_init(str, (guf_str_view){.str = c_str, .len = (ptrdiff_t)len}, alloc, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, GUF_ERR_MSG("in guf_str_try_init_from_cstr: guf_str_try_init failed"));
return NULL;
} else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
}
GUF_STR_KWRDS guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc)
{
return guf_str_try_init_from_cstr(str, c_str, alloc, NULL);
}
GUF_STR_KWRDS guf_str_view guf_str_to_view(const guf_str *str)
{
GUF_ASSERT(guf_str_is_valid(str));
guf_str_view sv = {
.str = guf_str_const_cstr(str),
.len = guf_str_len(str)
};
GUF_ASSERT(guf_str_view_is_valid(sv));
return sv;
}
GUF_STR_KWRDS char *guf_str_try_at(guf_str *str, ptrdiff_t idx, guf_err *err) GUF_STR_KWRDS char *guf_str_try_at(guf_str *str, ptrdiff_t idx, guf_err *err)
{ {
GUF_ASSERT_RELEASE(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
const ptrdiff_t len = guf_str_len(str); const ptrdiff_t len = guf_str_len(str);
@ -555,6 +686,7 @@ GUF_STR_KWRDS char *guf_str_try_at(guf_str *str, ptrdiff_t idx, guf_err *err)
} else { } else {
char *c_str = guf_str_try_get_cstr(str, err); char *c_str = guf_str_try_get_cstr(str, err);
if (err && *err != GUF_ERR_NONE) { if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, GUF_ERR_MSG("in guf_str_try_at: guf_str_try_get_cstr failed (guf_str is readonly)"));
return NULL; return NULL;
} }
GUF_ASSERT(c_str); GUF_ASSERT(c_str);
@ -576,6 +708,7 @@ GUF_STR_KWRDS char *guf_str_try_back(guf_str *str, guf_err *err)
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_back: len == 0")); guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_back: len == 0"));
return NULL; return NULL;
} else { } else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return guf_str_try_at(str, len - 1, err); return guf_str_try_at(str, len - 1, err);
} }
} }
@ -593,6 +726,7 @@ GUF_STR_KWRDS char *guf_str_try_front(guf_str *str, guf_err *err)
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_front: len == 0")); guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_front: len == 0"));
return NULL; return NULL;
} else { } else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return guf_str_try_at(str, 0, err); return guf_str_try_at(str, 0, err);
} }
} }
@ -604,8 +738,8 @@ GUF_STR_KWRDS char *guf_str_front(guf_str *str)
GUF_STR_KWRDS char guf_str_try_at_cpy(const guf_str *str, ptrdiff_t idx, guf_err *err) GUF_STR_KWRDS char guf_str_try_at_cpy(const guf_str *str, ptrdiff_t idx, guf_err *err)
{ {
GUF_ASSERT(guf_str_is_valid(str));
const ptrdiff_t len = guf_str_len(str); const ptrdiff_t len = guf_str_len(str);
if (idx < 0) { if (idx < 0) {
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_at_cpy: idx < 0")); guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_at_cpy: idx < 0"));
return '\0'; return '\0';
@ -633,6 +767,7 @@ GUF_STR_KWRDS char guf_str_try_back_cpy(const guf_str *str, guf_err *err)
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_back_cpy: len == 0")); guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_back_cpy: len == 0"));
return '\0'; return '\0';
} else { } else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return guf_str_try_at_cpy(str, len - 1, err); return guf_str_try_at_cpy(str, len - 1, err);
} }
} }
@ -650,6 +785,7 @@ GUF_STR_KWRDS char guf_str_try_front_cpy(const guf_str *str, guf_err *err)
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_front_cpy: len == 0")); guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_front_cpy: len == 0"));
return '\0'; return '\0';
} else { } else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return guf_str_try_at_cpy(str, 0, err); return guf_str_try_at_cpy(str, 0, err);
} }
} }
@ -763,17 +899,55 @@ GUF_STR_KWRDS int guf_str_cmp(const guf_str *a, const guf_str *b)
return memcmp(a_cstr, b_cstr, shorter_len); return memcmp(a_cstr, b_cstr, shorter_len);
} }
GUF_STR_KWRDS guf_str *guf_str_try_set(guf_str *str, guf_str_view sv, guf_err *err)
{
GUF_ASSERT(guf_str_is_valid(str));
if (guf_str_is_readonly(str)) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in guf_str_try_set: guf_str is readonly"));
return NULL;
} else if (!guf_str_view_is_valid(sv)) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in guf_str_try_set: str_view is invalid"));
return NULL;
}
guf_str_try_reserve(str, sv.len, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, GUF_ERR_MSG("in guf_str_try_set: guf_str_try_reserve failed"));
return NULL;
}
char *c_str_dst = guf_str_cstr(str);
GUF_ASSERT(c_str_dst);
if (sv.len > 0) {
GUF_ASSERT(sv.str);
memcpy(c_str_dst, sv.str, sv.len);
}
c_str_dst[sv.len] = '\0';
guf_str_set_len_internal_(str, sv.len);
GUF_ASSERT(guf_str_is_valid(str));
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
GUF_STR_KWRDS guf_str *guf_str_set(guf_str *str, guf_str_view sv)
{
return guf_str_try_set(str, sv, NULL);
}
GUF_STR_KWRDS guf_str *guf_str_try_append_char(guf_str *str, char c, ptrdiff_t times, guf_err *err) GUF_STR_KWRDS guf_str *guf_str_try_append_char(guf_str *str, char c, ptrdiff_t times, guf_err *err)
{ {
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
if (guf_str_is_readonly(str)) { if (guf_str_is_readonly(str)) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_append_char: str is readonly"); guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, GUF_ERR_MSG("in guf_str_try_append_char: str is readonly"));
return NULL; return NULL;
} }
if (times < 0) { if (times < 0) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, "in guf_str_try_append_char: repeats < 0"); guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in guf_str_try_append_char: repeats < 0"));
return NULL; return NULL;
} else if (times == 0) { } else if (times == 0) {
guf_err_set_if_not_null(err, GUF_ERR_NONE); guf_err_set_if_not_null(err, GUF_ERR_NONE);
@ -787,12 +961,12 @@ GUF_STR_KWRDS guf_str *guf_str_try_append_char(guf_str *str, char c, ptrdiff_t t
const size_t new_len = old_len + (size_t)times; const size_t new_len = old_len + (size_t)times;
if (new_len <= old_len || new_len >= (size_t)PTRDIFF_MAX) { // Handle overflow. if (new_len <= old_len || new_len >= (size_t)PTRDIFF_MAX) { // Handle overflow.
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, "in guf_str_try_append_char: new length would overflow ptrdiff_t"); guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in guf_str_try_append_char: new length would overflow ptrdiff_t"));
return NULL; return NULL;
} else if (new_len > old_cap) { // Need to grow capacity. } else if (new_len > old_cap) { // Need to grow capacity.
guf_str_try_reserve(str, new_len, err); guf_str_try_reserve(str, new_len, err);
if (err && *err != GUF_ERR_NONE) { if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_str_try_append_char: failed to reserve capacity"); guf_err_set_or_panic(err, *err, GUF_ERR_MSG("in guf_str_try_append_char: failed to reserve capacity"));
return NULL; return NULL;
} }
} }
@ -833,10 +1007,10 @@ GUF_STR_KWRDS guf_str *guf_str_try_append(guf_str *str, guf_str_view sv, guf_err
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
if (!guf_str_view_is_valid(sv)) { if (!guf_str_view_is_valid(sv)) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, "in guf_str_try_append_view: str_view is invalid"); guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in guf_str_try_append_view: str_view is invalid"));
return NULL; return NULL;
} else if (guf_str_is_readonly(str)) { } else if (guf_str_is_readonly(str)) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in in guf_str_try_append_view: str is readonly"); guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, GUF_ERR_MSG("in in guf_str_try_append_view: str is readonly"));
return NULL; return NULL;
} }
@ -850,12 +1024,12 @@ GUF_STR_KWRDS guf_str *guf_str_try_append(guf_str *str, guf_str_view sv, guf_err
const size_t old_len = guf_str_len_internal_(str); const size_t old_len = guf_str_len_internal_(str);
const size_t new_len = old_len + (size_t)sv.len; const size_t new_len = old_len + (size_t)sv.len;
if (new_len <= old_len || new_len >= (size_t)PTRDIFF_MAX) { // Handle overflow. if (new_len <= old_len || new_len >= (size_t)PTRDIFF_MAX) { // Handle overflow.
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, "in guf_str_try_append_view: new length would overflow ptrdiff_t"); guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in guf_str_try_append_view: new length would overflow ptrdiff_t"));
return NULL; return NULL;
} else if (new_len > old_cap) { // Growth necessary. } else if (new_len > old_cap) { // Growth necessary.
guf_str_try_reserve(str, new_len, err); guf_str_try_reserve(str, new_len, err);
if (err && *err != GUF_ERR_NONE) { if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_str_try_append_view: failed to reserve capacity"); guf_err_set_or_panic(err, *err, GUF_ERR_MSG("in guf_str_try_append_view: failed to reserve capacity"));
return NULL; return NULL;
} }
} }
@ -882,55 +1056,6 @@ GUF_STR_KWRDS guf_str *guf_str_append(guf_str *str, guf_str_view sv)
return guf_str_try_append(str, sv, NULL); return guf_str_try_append(str, sv, NULL);
} }
GUF_STR_KWRDS guf_str *guf_str_try_append_cstr(guf_str *str, const char *c_str, guf_err *err)
{
GUF_ASSERT(guf_str_is_valid(str));
if (!c_str) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_append_cstr: c_str is NULL");
return NULL;
} else if (guf_str_is_readonly(str)) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_append_cstr: str is readonly");
return NULL;
}
char *dst_cstr = guf_str_get_cstr_internal_(str);
size_t i = 0;
do {
size_t cap = guf_str_cap_internal_(str);
size_t len = guf_str_len_internal_(str);
GUF_ASSERT(len <= cap);
if (len == cap) { // Grow if necessary.
if (cap == PTRDIFF_MAX) {
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, "in guf_str_try_append_cstr: cannot grow (capacity is PTRDIFF_MAX)");
return NULL;
}
guf_str_try_reserve(str, cap + 1, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, "in guf_str_try_append_cstr: failed to reserve");
return NULL;
}
cap = guf_str_cap_internal_(str);
len = guf_str_len_internal_(str);
}
dst_cstr[len] = c_str[i];
guf_str_set_len_internal_(str, len + 1);
} while (c_str[i++] != '\0');
GUF_ASSERT(guf_str_is_valid(str));
GUF_ASSERT(dst_cstr[guf_str_len_internal_(str)] == '\0');
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
GUF_STR_KWRDS guf_str *guf_str_append_cstr(guf_str *str, const char *c_str)
{
return guf_str_try_append_cstr(str, c_str, NULL);
}
GUF_STR_KWRDS guf_str *guf_str_try_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count, guf_err *err) GUF_STR_KWRDS guf_str *guf_str_try_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count, guf_err *err)
{ {
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
@ -975,6 +1100,33 @@ GUF_STR_KWRDS guf_str *guf_str_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t cou
return guf_str_try_substr(str, pos, count, NULL); return guf_str_try_substr(str, pos, count, NULL);
} }
GUF_STR_KWRDS char guf_str_try_pop_back(guf_str *str, guf_err *err)
{
GUF_ASSERT(guf_str_is_valid(str));
const ptrdiff_t len = guf_str_len(str);
if (len <= 0) {
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in guf_str_try_pop_back: len <= 0"));
return '\0';
}
GUF_ASSERT(len - 1 >= 0);
const char last = guf_str_at_cpy(str, len - 1);
guf_str_try_substr(str, 0, len - 1, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, GUF_ERR_MSG("in guf_str_try_pop_back: guf_str_try_substr failed"));
return '\0';
} else {
GUF_ASSERT(guf_str_is_valid(str));
GUF_ASSERT(guf_str_len(str) == len - 1);
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return last;
}
}
GUF_STR_KWRDS char guf_str_pop_back(guf_str *str)
{
return guf_str_try_pop_back(str, NULL);
}
// guf_str_view: // guf_str_view:

View File

@ -11,6 +11,7 @@ extern "C" {
#include "test_dbuf.hpp" #include "test_dbuf.hpp"
#include "test_dict.hpp" #include "test_dict.hpp"
#include "test_utf8.hpp" #include "test_utf8.hpp"
#include "test_str.hpp"
std::unordered_set<std::unique_ptr<Test>> g_tests {}; std::unordered_set<std::unique_ptr<Test>> g_tests {};
@ -31,6 +32,10 @@ void init_tests()
test = std::make_unique<UTF8Test>("UTF8Test"); test = std::make_unique<UTF8Test>("UTF8Test");
GUF_ASSERT_RELEASE(test.get()); GUF_ASSERT_RELEASE(test.get());
g_tests.insert(std::move(test)); g_tests.insert(std::move(test));
test = std::make_unique<StrTest>("StrTest");
GUF_ASSERT_RELEASE(test.get())
g_tests.insert(std::move(test));
} }
int main() int main()

212
src/test/test_str.hpp Normal file
View File

@ -0,0 +1,212 @@
#include <vector>
#include <string>
#include "test.hpp"
extern "C" {
#include "guf_alloc_libc.h"
#include "guf_str.h"
}
struct StrTest : public Test
{
public:
StrTest(const std::string& name) : Test(name) {};
private:
void test_init_free(std::string str)
{
guf_str s0;
guf_str_init(&s0, GUF_CSTR_TO_VIEW_CPP(str.c_str()), &guf_allocator_libc);
guf_str s1 = guf_str_new(GUF_CSTR_TO_VIEW_CPP(str.c_str()), &guf_allocator_libc);
guf_str s2;
guf_str_init_from_cstr(&s2, str.c_str(), &guf_allocator_libc);
TEST_CHECK(guf_str_equal(&s0, &s1));
TEST_CHECK(guf_str_equal(&s0, &s2));
TEST_CHECK(guf_str_equal(&s1, &s2));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s0));
TEST_CHECK(str == guf_str_const_cstr(&s0));
TEST_CHECK(str == guf_str_cstr(&s0));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s1));
TEST_CHECK(str == guf_str_const_cstr(&s1));
TEST_CHECK(str == guf_str_cstr(&s1));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s2));
TEST_CHECK(str == guf_str_const_cstr(&s2));
TEST_CHECK(str == guf_str_cstr(&s2));
guf_str_free(&s0, NULL);
guf_str_free(&s1, NULL);
guf_str_free(&s2, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
TEST_CHECK(guf_str_is_uninit(&s1));
TEST_CHECK(guf_str_is_uninit(&s2));
}
void test_init_empty()
{
std::string str = "";
guf_str s = GUF_STR_UNINITIALISED_CPP;
guf_str_init_empty(&s, &guf_allocator_libc);
TEST_CHECK(guf_str_len(&s) == 0);
TEST_CHECK(str == guf_str_const_cstr(&s));
guf_str_append_char(&s, 'a', 1024);
str.append(1024, 'a');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_str_append_char(&s, 'b', 24);
str.append(24, 'b');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_str_append_char(&s, 'c', 255);
str.append(255, 'c');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
*guf_str_at(&s, 0) = '<';
str.at(0) = '<';
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
*guf_str_at(&s, guf_str_len(&s) - 1) = '>';
str.at(str.size() - 1) = '>';
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_err err = GUF_ERR_NONE;
TEST_CHECK(NULL == guf_str_try_at(&s, guf_str_len(&s), &err));
TEST_CHECK(err != GUF_ERR_NONE && err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
TEST_CHECK(NULL == guf_str_try_at(&s, -1, &err));
TEST_CHECK(err != GUF_ERR_NONE && err == GUF_ERR_IDX_RANGE);
guf_str_free(&s, NULL);
TEST_CHECK(guf_str_is_uninit(&s));
}
void test_append_char(std::string str, bool include_null = false)
{
guf_str s0 = guf_str_new(guf_str_view{.str = str.c_str(), .len = (ptrdiff_t)str.size()}, &guf_allocator_libc);
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s0));
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
for (int i = include_null ? 0 : 1; i < 128; ++i) {
char ch = (char)i;
guf_str_append_one_char(&s0, ch);
str.append(1, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
for (int i = include_null ? 0 : 1; i < 128; ++i) {
char ch = (char)i;
guf_str_append_char(&s0, ch, i);
str.append(i, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
guf_str_append_char(&s0, ch, i * 16);
str.append(i * 16, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
guf_str_free(&s0, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
}
void append_str(const std::string& a, const std::string& b)
{
std::string str0 = a;
guf_str s0 = guf_str_new(guf_str_view{.str = str0.c_str(), .len = (ptrdiff_t)str0.size()}, &guf_allocator_libc);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str0.size());
TEST_CHECK((str0 == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
TEST_CHECK((str0 == std::string_view{guf_str_cstr(&s0), (size_t)guf_str_len(&s0)}));
for (int i = 0; i <= 64; ++i) {
str0.append(b);
guf_str_append(&s0, guf_str_view{.str = b.c_str(), .len = (ptrdiff_t)b.size()});
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str0.size());
TEST_CHECK((str0 == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
TEST_CHECK((str0 == std::string_view{guf_str_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
guf_str_free(&s0, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
}
public:
bool run()
{
if (done) {
return passed;
}
const std::vector<std::string> words = {
"",
"\0",
"Hello",
"Othell\0o",
"f\0\0",
"\0",
"0",
"a",
"ab",
"🌈 waow a rainboge!",
"orange cat(1) :3",
"xes yag",
"Hello, world! This is a pretty darn long string I'd say...",
"I want to eat crayons. I crave crayons because they are tasty, and everybody telling me crayons are not edible must be either lying or dumb. I like trains. 42 is a number. 3.14159265... is not a rational number, and it is called pi. I ate some pie (it was a crayon pie).",
std::string(32, 'a'),
std::string(64, 'b'),
std::string(1024, 'a'),
std::string(2048, 'a'),
std::string(4096, 'a'),
std::string(5001, 'a'),
std::string(7121, 'a'),
std::string(2000, 'a'),
std::string(GUF_STR_SSO_BUF_CAP, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 1, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 1, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 4, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 5, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 6, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 7, 'a'),
};
test_init_empty();
for (const auto& word : words) {
test_init_free(word);
test_append_char(word);
test_append_char(word, true);
}
for (size_t i = 0; i < words.size(); ++i) {
const auto& w1 = words.at(i);
append_str(w1, w1);
append_str(w1, w1);
for (size_t j = i + 1; j < words.size(); ++j) {
const auto& w2 = words.at(j);
append_str(w1, w2);
append_str(w2, w1);
}
}
done = true;
passed = (num_failed_checks == 0);
return passed;
}
};

View File

@ -9,7 +9,6 @@
- guf_stack, guf_queue, guf_dqueue, guf_prio_queue (using a heap), guf_ringbuf - guf_stack, guf_queue, guf_dqueue, guf_prio_queue (using a heap), guf_ringbuf
- track allocs for test (implement alloc tracker): - track allocs for test (implement alloc tracker):
- each thread needs its own alloc and alloc_ctx; don't track granular, give each allocator it's unique id maybe? - each thread needs its own alloc and alloc_ctx; don't track granular, give each allocator it's unique id maybe?