Fix guf_str bugs and add tests
This commit is contained in:
parent
0910ee4bd8
commit
e98dc3b91e
334
src/guf_str.h
334
src/guf_str.h
@ -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:
|
||||||
|
|||||||
@ -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
212
src/test/test_str.hpp
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
1
todo.txt
1
todo.txt
@ -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?
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user