545 lines
21 KiB
C++
545 lines
21 KiB
C++
#if defined(GUF_STR_IMPL_STATIC)
|
|
#define GUF_STR_KWRDS static
|
|
#else
|
|
#define GUF_STR_KWRDS
|
|
#endif
|
|
|
|
#ifndef GUF_STR_H
|
|
#define GUF_STR_H
|
|
#include "guf_common.h"
|
|
#include "guf_alloc.h"
|
|
#include "guf_str_view_type.h"
|
|
#include "guf_utf8.h"
|
|
#include "guf_hash.h"
|
|
|
|
// cf. libc++ short-string optimisation: https://joellaity.com/2020/01/31/string.html (last-retrieved 2025-03-10)
|
|
|
|
typedef struct guf_str_internal_long_ {
|
|
size_t capacity; // If long string: capacity's least significant bit always set to 1 (or its most significant bit for big-endian platforms)
|
|
size_t size;
|
|
char *c_str;
|
|
} guf_str_internal_long_;
|
|
|
|
#define GUF_STR_SSO_BUF_CAP (sizeof(guf_str_internal_long_) - sizeof(unsigned char)) /* 23 bytes on 64-bit platforms, 11 bytes on 32-bit platforms */
|
|
|
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || (defined(__cplusplus) && __cplusplus >= 201103L)
|
|
static_assert(GUF_STR_SSO_BUF_CAP > 0, "GUF_STR_SSO_BUF_CAP < 0 (this is very weird)"); // Basically cannot fail.
|
|
static_assert(GUF_STR_SSO_BUF_CAP < 0x80, "GUF_STR_SSO_BUF_CAP >= 128 (no support for platforms with wordsize >= 512-bits)"); // Could fail on hypothetical platforms with 512-bit wordsize (and above).
|
|
#endif
|
|
|
|
typedef struct guf_str_internal_short_ {
|
|
unsigned char size; // size overlaps with the first byte of guf_str_internal_long_.capacity [1]
|
|
char c_str[GUF_STR_SSO_BUF_CAP];
|
|
} guf_str_internal_short_;
|
|
|
|
/*
|
|
[1] The first byte of guf_str_internal_long_.capacity is its least-significant-byte on little-endian
|
|
platforms, and its most-significant byte on big-endian platforms.
|
|
*/
|
|
|
|
typedef struct guf_str {
|
|
union {
|
|
guf_str_internal_long_ lng;
|
|
guf_str_internal_short_ shrt;
|
|
} data; // 24 bytes on 64-bit platforms, 12 bytes on 32-bit platforms.
|
|
guf_allocator *allocator; // Wasteful (8 bytes on 64-bit platforms...), but keeping this pointer also allows us to have "read-only strings" (a string is read-only if allocator == NULL)
|
|
} guf_str; // Total: 32 bytes on 64-bit platforms, 16 bytes on 32-bit platforms.
|
|
|
|
#define GUF_CSTR_TO_VIEW(CSTR) ((guf_str_view){.str = (CSTR), .len = (ptrdiff_t)strlen((CSTR))})
|
|
#define GUF_STR_TO_VIEW(GUF_STR_PTR) ((guf_str_view){.str = guf_str_const_cstr((GUF_STR_PTR)), .len = (ptrdiff_t)guf_str_len((GUF_STR_PTR))})
|
|
#define GUF_CSTR_TO_READONLY_STR(CSTR) ((guf_str){.allocator = NULL, .data.lng.c_str = (CSTR), .data.lng.size = strlen(CSTR) + 1, .data.lng.capacity = 0})
|
|
#ifdef __cplusplus
|
|
// Standard C++ does not have compound literals like C99...
|
|
#define GUF_CSTR_TO_VIEW_CPP(CSTR) guf_str_view{.str = (CSTR), .len = (ptrdiff_t)strlen(CSTR)}
|
|
#endif
|
|
|
|
// guf_str_view:
|
|
GUF_STR_KWRDS bool guf_str_view_is_valid(guf_str_view sv);
|
|
GUF_STR_KWRDS guf_str guf_str_substr_cpy(guf_str_view str, ptrdiff_t pos, size_t count); // not necessary
|
|
GUF_STR_KWRDS guf_str_view guf_substr_view(guf_str_view str, ptrdiff_t pos, ptrdiff_t count);
|
|
|
|
GUF_STR_KWRDS guf_str_view guf_str_view_trim_left(guf_str_view str);
|
|
GUF_STR_KWRDS guf_str_view guf_str_view_trim_right(guf_str_view str);
|
|
|
|
GUF_STR_KWRDS guf_hash_size_t guf_str_view_hash(const guf_str_view *sv);
|
|
GUF_STR_KWRDS uint64_t guf_str_view_hash64(const guf_str_view *sv);
|
|
GUF_STR_KWRDS uint32_t guf_str_view_hash32(const guf_str_view *sv);
|
|
|
|
GUF_STR_KWRDS bool guf_str_view_equal(const guf_str_view* a, const guf_str_view* b);
|
|
GUF_STR_KWRDS bool guf_str_view_equal_val_arg(guf_str_view a_val, guf_str_view b_val);
|
|
GUF_STR_KWRDS int guf_str_view_cmp(const void *str_view_a, const void *str_view_b); // For qsort etc.
|
|
|
|
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_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_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_empty(guf_str *str, guf_allocator *alloc);
|
|
|
|
GUF_STR_KWRDS guf_str guf_str_try_new(guf_str_view str_view, guf_allocator *alloc, guf_err *err);
|
|
GUF_STR_KWRDS guf_str guf_str_new(guf_str_view str_view, guf_allocator *alloc);
|
|
GUF_STR_KWRDS guf_str guf_str_try_new_from_cstr(const char *c_str, guf_allocator *alloc, guf_err *err);
|
|
GUF_STR_KWRDS guf_str guf_str_new_from_cstr(const char *c_str, guf_allocator *alloc);
|
|
GUF_STR_KWRDS guf_str guf_str_new_empty(guf_allocator *alloc);
|
|
|
|
GUF_STR_KWRDS guf_str guf_str_try_new_substr(guf_str_view str_view, ptrdiff_t pos, ptrdiff_t len, guf_allocator *alloc, guf_err *err);
|
|
GUF_STR_KWRDS guf_str guf_str_new_substr(guf_str_view str_view, ptrdiff_t pos, ptrdiff_t len, guf_allocator *alloc);
|
|
|
|
GUF_STR_KWRDS bool guf_str_equal(const guf_str *a, const guf_str *b);
|
|
GUF_STR_KWRDS bool guf_str_equals_cstr(const guf_str *a, const char *c_str);
|
|
GUF_STR_KWRDS bool guf_str_equals_strview(const guf_str *a, guf_str_view b);
|
|
|
|
GUF_STR_KWRDS void guf_str_free(guf_str *str, void *ctx);
|
|
|
|
GUF_STR_KWRDS guf_str *guf_str_append(guf_str *str, guf_str_view to_append);
|
|
GUF_STR_KWRDS guf_str *guf_str_append_cstr(guf_str *str, const char *cstr_to_append); // Not necessary
|
|
GUF_STR_KWRDS guf_str *guf_str_substr(guf_str* str, size_t pos, size_t count);
|
|
|
|
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);
|
|
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);
|
|
|
|
GUF_STR_KWRDS char guf_str_pop_back(guf_str *str);
|
|
GUF_STR_KWRDS char guf_str_pop_front(guf_str *str);
|
|
|
|
GUF_STR_KWRDS char *guf_str_at(guf_str *str, size_t idx);
|
|
GUF_STR_KWRDS char *guf_str_back(guf_str *str);
|
|
GUF_STR_KWRDS char *guf_str_front(guf_str *str);
|
|
GUF_STR_KWRDS const char *guf_str_const_cstr(const guf_str *str);
|
|
|
|
GUF_STR_KWRDS size_t guf_str_len(const guf_str *str); // The length (in chars) without the final zero-terminator.
|
|
GUF_STR_KWRDS size_t guf_str_capacity(const guf_str *str); // The capacity (in chars) without the final zero-terminator.
|
|
|
|
GUF_STR_KWRDS bool guf_str_is_short(const guf_str *str);
|
|
GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str);
|
|
#endif
|
|
|
|
// #define GUF_STR_IMPL_STATIC /*debug*/
|
|
|
|
#if defined(GUF_STR_IMPL) || defined(GUF_STR_IMPL_STATIC)
|
|
|
|
#ifdef __cplusplus
|
|
#error "Must compile guf_str as C99 (or above) because type-punning with unions is undefined behaviour in C++"
|
|
#endif
|
|
|
|
#include "guf_common.h"
|
|
#include <string.h>
|
|
|
|
#ifdef GUF_STR_IMPL
|
|
#define GUF_UTF8_IMPL
|
|
#else
|
|
#define GUF_UTF8_IMPL_STATIC
|
|
#endif
|
|
#include "guf_utf8.h"
|
|
|
|
// TODO: find_first_of
|
|
|
|
// guf_str:
|
|
#if defined(GUF_PLATFORM_LITTLE_ENDIAN)
|
|
#define GUF_STR_IS_LONG_MASK ((unsigned char)1) /* binary 0000.0001 */
|
|
#define GUF_STR_GET_CAP_MASK (~(size_t)1) /* binary 1111.1111 (1111.1111)* 1111.1110 */
|
|
|
|
static inline void guf_str_set_lng_cap_(guf_str *str, size_t capacity)
|
|
{
|
|
GUF_ASSERT(capacity % 2 == 0);
|
|
GUF_ASSERT(capacity > GUF_STR_SSO_BUF_CAP);
|
|
str->data.lng.capacity = capacity | ((size_t)1);
|
|
}
|
|
static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size)
|
|
{
|
|
GUF_ASSERT(size < GUF_STR_SSO_BUF_CAP && size < 0x80);
|
|
str->data.shrt.size = (unsigned char)(size << 1u);
|
|
}
|
|
#elif defined(GUF_PLATFORM_BIG_ENDIAN)
|
|
#define GUF_STR_IS_LONG_MASK ((unsigned char)0x80) /* binary 1000 0000 */
|
|
#define GUF_STR_GET_CAP_MASK ((size_t)SIZE_T_MAX >> 1u) /* binary 0111.1111 (1111.1111)* 1111.1111 */
|
|
|
|
static inline void guf_str_set_lng_cap_(guf_str *str, size_t capacity)
|
|
{
|
|
GUF_ASSERT(capacity % 2 == 0);
|
|
GUF_ASSERT(capacity > GUF_STR_SSO_BUF_CAP);
|
|
str->data.lng.capacity = ~GUF_STR_GET_CAP_MASK | (capacity >> 1);
|
|
}
|
|
static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size)
|
|
{
|
|
GUF_ASSERT(size < GUF_STR_SSO_BUF_CAP && size < 0x80);
|
|
str->data.shrt.size = size;
|
|
}
|
|
#else
|
|
#error "guf_str: neither GUF_PLATFORM_LITTLE_ENDIAN nor GUF_PLATFORM_BIG_ENDIAN is defined"
|
|
#endif
|
|
|
|
static bool guf_str_is_valid(const guf_str *str)
|
|
{
|
|
GUF_ASSERT(str);
|
|
const bool is_readonly = !str->allocator;
|
|
if (is_readonly) {
|
|
bool valid_readonly = str->data.lng.c_str && str->data.lng.capacity == 0 && str->data.lng.size > 0;
|
|
return valid_readonly;
|
|
}
|
|
const bool valid_allocator = str->allocator && str->allocator->alloc && str->allocator->free && str->allocator->realloc;
|
|
if (!valid_allocator) {
|
|
return false;
|
|
}
|
|
|
|
const unsigned char first_byte = str->data.shrt.size; // union type-punning (only legal in C99 and above; undefined behaviour in C++ I think).
|
|
const bool is_short = (first_byte & GUF_STR_IS_LONG_MASK) == 0;
|
|
|
|
if (is_short) {
|
|
const size_t size = (str->data.shrt.size >> 1);
|
|
return size > 0 && size <= GUF_STR_SSO_BUF_CAP && str->data.shrt.c_str[size - 1] == '\0';
|
|
} else {
|
|
const size_t cap_with_null = str->data.lng.capacity & ~(size_t)1;
|
|
return str->data.lng.c_str && cap_with_null > GUF_STR_SSO_BUF_CAP && str->data.lng.size > 0 && str->data.lng.size <= cap_with_null;
|
|
}
|
|
}
|
|
|
|
GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str)
|
|
{
|
|
GUF_ASSERT(guf_str_is_valid(str));
|
|
return !str->allocator;
|
|
}
|
|
|
|
GUF_STR_KWRDS bool guf_str_is_short(const guf_str *str)
|
|
{
|
|
GUF_ASSERT(guf_str_is_valid(str));
|
|
if (guf_str_is_readonly(str)) {
|
|
return false;
|
|
}
|
|
const unsigned char first_byte = str->data.shrt.size; // union type-punning (only legal in C99 and above; undefined behaviour in C++ I think).
|
|
return (first_byte & GUF_STR_IS_LONG_MASK) == 0;
|
|
}
|
|
|
|
GUF_STR_KWRDS size_t guf_str_capacity(const guf_str *str)
|
|
{
|
|
GUF_ASSERT(guf_str_is_valid(str));
|
|
if (guf_str_is_short(str)) {
|
|
return GUF_STR_SSO_BUF_CAP - 1;
|
|
} else if (guf_str_is_readonly(str)) {
|
|
return 0;
|
|
} else {
|
|
// Precondition: all capacities for data.lng must be even.
|
|
#if defined(GUF_PLATFORM_LITTLE_ENDIAN)
|
|
GUF_ASSERT(str->data.lng.capacity & ~GUF_STR_GET_CAP_MASK); // Assert the is_long bit is actually set.
|
|
const size_t cap_with_null = str->data.lng.capacity & GUF_STR_GET_CAP_MASK;
|
|
#elif defined(GUF_PLATFORM_BIG_ENDIAN)
|
|
GUF_ASSERT(str->data.lng.capacity & ~GUF_STR_GET_CAP_MASK); // Assert the is_long bit is actually set.
|
|
const size_t cap_with_null = (str->data.lng.capacity & GUF_STR_GET_CAP_MASK) << 1;
|
|
#endif
|
|
GUF_ASSERT(cap_with_null > 0 && cap_with_null > GUF_STR_SSO_BUF_CAP);
|
|
return cap_with_null - 1;
|
|
}
|
|
}
|
|
|
|
GUF_STR_KWRDS size_t guf_str_len(const guf_str *str)
|
|
{
|
|
GUF_ASSERT(guf_str_is_valid(str));
|
|
if (guf_str_is_short(str)) {
|
|
GUF_ASSERT(str->data.shrt.size > 0);
|
|
#if defined(GUF_PLATFORM_LITTLE_ENDIAN)
|
|
const size_t size = (str->data.shrt.size >> 1);
|
|
#elif defined(GUF_PLATFORM_BIG_ENDIAN)
|
|
const size_t size = (str->data.shrt.size);
|
|
#endif
|
|
GUF_ASSERT(size > 0 && size <= GUF_STR_SSO_BUF_CAP);
|
|
return size - 1;
|
|
} else {
|
|
const size_t size = str->data.lng.size;
|
|
GUF_ASSERT(size > 0);
|
|
return size - 1;
|
|
}
|
|
}
|
|
|
|
// GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t new_cap, guf_err *err)
|
|
// {
|
|
// GUF_ASSERT_RELEASE(guf_str_is_valid_writable(str));
|
|
|
|
// const ptrdiff_t size = guf_str_len(str) + 1;
|
|
// const ptrdiff_t old_cap = guf_str_capacity(str);
|
|
|
|
// if (new_cap <= old_cap) { // Growth not necessary.
|
|
// guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
|
// return str;
|
|
// }
|
|
|
|
// if (guf_str_is_short(str)) { // a.) Was short string -> need initial allocation.
|
|
// GUF_ASSERT(size == GUF_STR_SSO_BUFCAP);
|
|
// char *c_str_new = str->allocator->alloc(new_cap, str->allocator->ctx);
|
|
// if (!c_str_new) {
|
|
// guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, "in guf_str_try_grow_if_necessary: Initial allocation failed.");
|
|
// return NULL;
|
|
// }
|
|
// memcpy(c_str_new, str->data.stack.c_str, str->data.stack.size);
|
|
// str->state = GUF_STR_STATE_INIT;
|
|
// str->data.heap.c_str = c_str_new;
|
|
// str->data.heap.size = size;
|
|
// str->data.heap.capacity = new_cap;
|
|
// guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
|
// return str;
|
|
// }
|
|
// // b.) Was already allocated -> need to grow existing allocation.
|
|
// char *c_str_new = str->allocator->realloc(str->data.heap.c_str, old_cap, new_cap, str->allocator->ctx);
|
|
// if (!c_str_new) {
|
|
// guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, "in guf_str_try_grow_if_necessary: re-allocation failed.");
|
|
// return NULL;
|
|
// }
|
|
// str->data.heap.c_str = c_str_new;
|
|
// str->data.heap.capacity = new_cap;
|
|
// guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
|
// 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)
|
|
// {
|
|
// if (!str) {
|
|
// guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_init: str is NULL");
|
|
// return NULL;
|
|
// } else if (!alloc || !alloc->alloc || !alloc->realloc || !alloc->free) {
|
|
// guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_init: alloc (or allocs function pointers) is/are NULL");
|
|
// return NULL;
|
|
// }
|
|
|
|
// if (!guf_str_view_is_valid(str_view)) {
|
|
// guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_init: invalid str_view");
|
|
// return NULL;
|
|
// }
|
|
|
|
// str->allocator = alloc;
|
|
// str->state = GUF_STR_STATE_SHORT;
|
|
// str->data.stack.size = 1;
|
|
// str->data.stack.c_str[0] = '\0';
|
|
|
|
// if (str_view.len == PTRDIFF_MAX) {
|
|
// guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_init: str_view.len + 1 would overflow ptrdiff_t");
|
|
// return NULL;
|
|
// }
|
|
// const ptrdiff_t size = str_view.len + 1;
|
|
// GUF_ASSERT(size > 0);
|
|
|
|
// if (size <= GUF_STR_SSO_BUFCAP) { // a) Fits in short-string.
|
|
// str->state = GUF_STR_STATE_SHORT;
|
|
// GUF_ASSERT(size <= UCHAR_MAX);
|
|
// str->data.stack.size = (unsigned char)size;
|
|
// memcpy(str->data.stack.c_str, str_view.str, str_view.len);
|
|
// str->data.stack.c_str[str_view.len] = '\0';
|
|
// guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
|
// return str;
|
|
// }
|
|
// // b) Needs initial allocation.
|
|
// guf_str_try_reserve(str, size, err);
|
|
// if (err && *err != GUF_ERR_NONE) {
|
|
// guf_panic(*err, "in guf_str_try_init: Initial allocation failed");
|
|
// return NULL;
|
|
// }
|
|
// GUF_ASSERT(!guf_str_is_short(str));
|
|
// memcpy(str->data.heap.c_str, str_view.str, str_view.len);
|
|
// str->data.heap.c_str[str_view.len] = '\0';
|
|
// guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
|
// return str;
|
|
// }
|
|
|
|
|
|
|
|
// guf_str_view:
|
|
|
|
GUF_STR_KWRDS bool guf_str_view_is_valid(guf_str_view sv)
|
|
{
|
|
if (sv.str) {
|
|
return sv.len >= 0;
|
|
} else {
|
|
return sv.len == 0;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (input->len <= 0 || input->str == NULL) {
|
|
return (guf_str_view){.str = NULL, .len = 0};
|
|
}
|
|
|
|
ptrdiff_t max_delim_len = -1;
|
|
for (ptrdiff_t i = 0; i < num_delims; ++i) {
|
|
if (delims[i].len > max_delim_len) {
|
|
max_delim_len = delims[i].len;
|
|
}
|
|
}
|
|
|
|
guf_str_view tok = {.str = input->str, .len = 0};
|
|
guf_str_view prev_input = *input;
|
|
guf_utf8_char ch = {0};
|
|
|
|
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, input); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, input)) {
|
|
if (stat != GUF_UTF8_READ_VALID) {
|
|
prev_input = *input;
|
|
continue;
|
|
}
|
|
|
|
const int num_bytes = guf_utf8_char_num_bytes(&ch);
|
|
|
|
for (ptrdiff_t delim_len = GUF_MIN(max_delim_len, prev_input.len); delim_len > 0; --delim_len) {
|
|
guf_str_view delim_candidate = guf_substr_view(prev_input, 0, delim_len);
|
|
for (ptrdiff_t delim_i = 0; delim_i < num_delims; ++delim_i) {
|
|
if (guf_str_view_equal(&delim_candidate, delims + delim_i)) { // Found delim.
|
|
bool preserved = false;
|
|
if (preserved_delims && num_preserved_delims > 0) {
|
|
for (ptrdiff_t preserved_i = 0; preserved_i < num_preserved_delims; ++preserved_i) {
|
|
if (guf_str_view_equal(&delim_candidate, preserved_delims + preserved_i)) {
|
|
preserved = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!preserved) {
|
|
input->len = prev_input.len - delim_len;
|
|
input->str = prev_input.len > 0 ? prev_input.str + delim_len : NULL;
|
|
GUF_ASSERT(input->len >= 0);
|
|
} else {
|
|
input->str -= num_bytes;
|
|
input->len += num_bytes;
|
|
}
|
|
|
|
if (tok.len == 0) {
|
|
if (preserved) {
|
|
input->str += num_bytes;
|
|
input->len -= num_bytes;
|
|
return delim_candidate;
|
|
}
|
|
tok.str = input->str;
|
|
goto end;
|
|
} else {
|
|
return tok;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tok.len += num_bytes;
|
|
|
|
end:;
|
|
prev_input = *input;
|
|
}
|
|
|
|
return tok;
|
|
}
|
|
|
|
GUF_STR_KWRDS guf_str_view guf_str_view_trim_left_ascii(guf_str_view sv)
|
|
{
|
|
if (sv.len <= 0 || sv.str == NULL) {
|
|
return sv;
|
|
}
|
|
|
|
for (; sv.len > 0 && guf_char_isspace_ascii(*sv.str); --sv.len, ++sv.str);
|
|
|
|
GUF_ASSERT(sv.len >= 0);
|
|
GUF_ASSERT(sv.len == 0 || !guf_char_isspace_ascii(*sv.str));
|
|
|
|
return sv;
|
|
}
|
|
|
|
GUF_STR_KWRDS guf_str_view guf_str_view_trim_right_ascii(guf_str_view sv)
|
|
{
|
|
if (sv.len <= 0 || sv.str == NULL) {
|
|
return sv;
|
|
}
|
|
|
|
for (; sv.len > 0 && guf_char_isspace_ascii(sv.str[sv.len - 1]); --sv.len);
|
|
|
|
GUF_ASSERT(sv.len >= 0);
|
|
GUF_ASSERT(sv.len == 0 || !guf_char_isspace_ascii(sv.str[sv.len - 1]));
|
|
|
|
return sv;
|
|
}
|
|
|
|
GUF_STR_KWRDS guf_str_view guf_str_view_trim_right(guf_str_view sv)
|
|
{
|
|
if (sv.len <= 0 || sv.str == NULL) {
|
|
return sv;
|
|
}
|
|
char c = sv.str[sv.len - 1];
|
|
while (sv.len > 0 && sv.str && c != ' ' && c != '\n' && c != '\t' && c != '\v' && c != '\f' && c != '\r') {
|
|
--sv.len;
|
|
++sv.str;
|
|
c = sv.str[0];
|
|
}
|
|
return sv;
|
|
}
|
|
|
|
GUF_STR_KWRDS guf_str_view guf_substr_view(guf_str_view str, ptrdiff_t pos, ptrdiff_t count)
|
|
{
|
|
GUF_ASSERT(str.str);
|
|
GUF_ASSERT(pos >= 0);
|
|
GUF_ASSERT(count >= 0);
|
|
|
|
if (str.len == 0 || count == 0 || pos >= str.len || str.str == NULL) {
|
|
return (guf_str_view){.str = str.str, .len = 0};
|
|
}
|
|
|
|
const ptrdiff_t substr_len = pos + count > str.len ? str.len - pos : count;
|
|
GUF_ASSERT(substr_len >= 0);
|
|
GUF_ASSERT(substr_len <= str.len);
|
|
|
|
return (guf_str_view){.str = str.str + pos, .len = substr_len};
|
|
}
|
|
|
|
GUF_STR_KWRDS guf_hash_size_t guf_str_view_hash(const guf_str_view *sv)
|
|
{
|
|
GUF_ASSERT(sv);
|
|
if (!sv->str || sv->len <= 0) {
|
|
return GUF_HASH_INIT;
|
|
}
|
|
return guf_hash(sv->str, sv->len, GUF_HASH_INIT);
|
|
}
|
|
|
|
GUF_STR_KWRDS uint64_t guf_str_view_hash64(const guf_str_view *sv)
|
|
{
|
|
GUF_ASSERT(sv);
|
|
if (!sv->str || sv->len <= 0) {
|
|
return GUF_HASH64_INIT;
|
|
}
|
|
return guf_hash64(sv->str, sv->len, GUF_HASH64_INIT);
|
|
}
|
|
|
|
GUF_STR_KWRDS uint32_t guf_str_view_hash32(const guf_str_view *sv)
|
|
{
|
|
GUF_ASSERT(sv);
|
|
if (!sv->str || sv->len <= 0) {
|
|
return GUF_HASH32_INIT;
|
|
}
|
|
return guf_hash32(sv->str, sv->len, GUF_HASH32_INIT);
|
|
}
|
|
|
|
GUF_STR_KWRDS bool guf_str_view_equal(const guf_str_view* a, const guf_str_view* b)
|
|
{
|
|
GUF_ASSERT(a && b);
|
|
if (a->len != b->len) {
|
|
return false;
|
|
}
|
|
|
|
if ((!a->str && b->str) || (!b->str && a->str)) {
|
|
return false;
|
|
} else if (!a->str && !b->str) {
|
|
return a->len == b->len;
|
|
}
|
|
GUF_ASSERT(a->str && b->str);
|
|
|
|
if (a->len <= 0) {
|
|
return true;
|
|
}
|
|
|
|
return 0 == memcmp(a->str, b->str, a->len);
|
|
}
|
|
|
|
GUF_STR_KWRDS bool guf_str_view_equal_val_arg(guf_str_view a_val, guf_str_view b_val)
|
|
{
|
|
return guf_str_view_equal(&a_val, &b_val);
|
|
}
|
|
|
|
#undef GUF_STR_IMPL
|
|
#undef GUF_STR_IMPL_STATIC
|
|
#endif /* end impl */
|
|
|
|
#undef GUF_STR_KWRDS
|