Implement guf_str

This commit is contained in:
jun 2025-03-17 21:15:53 +01:00
parent 9ddea4bb07
commit ad884ee1e9
7 changed files with 699 additions and 163 deletions

View File

@ -4,8 +4,8 @@
#include "guf_alloc.h" #include "guf_alloc.h"
typedef struct guf_libc_alloc_ctx { typedef struct guf_libc_alloc_ctx {
bool zero_init;
int alloc_type_id, thread_id; int alloc_type_id, thread_id;
bool zero_init;
} guf_libc_alloc_ctx; } guf_libc_alloc_ctx;
static inline void *guf_libc_alloc(ptrdiff_t size, void *ctx) static inline void *guf_libc_alloc(ptrdiff_t size, void *ctx)

View File

@ -2,6 +2,7 @@
#include "guf_alloc.h" #include "guf_alloc.h"
typedef struct guf_alloc_info { typedef struct guf_alloc_info {
const char *name;
ptrdiff_t allocated_bytes, freed_bytes; ptrdiff_t allocated_bytes, freed_bytes;
size_t alloc_count, realloc_count, free_count; size_t alloc_count, realloc_count, free_count;
} guf_alloc_info; } guf_alloc_info;

View File

@ -480,6 +480,7 @@ static inline bool GUF_CAT(GUF_DBUF_NAME, _copy_opt_available)(guf_cpy_opt cpy_o
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert)(GUF_DBUF_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err) GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert)(GUF_DBUF_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err)
{ {
GUF_ASSERT(GUF_CAT(GUF_DBUF_NAME,_valid)(dbuf)); GUF_ASSERT(GUF_CAT(GUF_DBUF_NAME,_valid)(dbuf));
GUF_ASSERT(elem);
if (idx < 0 || idx > dbuf->size) { if (idx < 0 || idx > dbuf->size) {
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_insert")); guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_insert"));

124
src/guf_id_pool.h Normal file
View File

@ -0,0 +1,124 @@
#if defined(GUF_ID_POOL_IMPL_STATIC)
#define GUF_ID_POOL_KWRDS static
#else
#define GUF_ID_POOL_KWRDS
#endif
#ifndef GUF_ID_POOL_H
#define GUF_ID_POOL_H
#include "guf_common.h"
#include "guf_assert.h"
#endif
// cf. https://github.com/erincatto/box2d/blob/main/src/id_pool.c (last-retrieved 2025-03-11)
// // test beg
#define GUF_ID_POOL_IMPL_STATIC
#define GUF_ID_POOL_T uint32_t
#define GUF_ID_POOL_NAME guf_idpool_u32
// // test end
#ifndef GUF_ID_POOL_T
#error "Must pass GUF_ID_POOL_T"
#endif
#ifndef GUF_ID_POOL_NAME
#error "Must pass GUF_ID_POOL_NAME"
#endif
#define GUF_ID_POOL_DBUF GUF_CAT(GUF_ID_POOL_NAME, _id_dbuf)
#define GUF_DBUF_NAME GUF_ID_POOL_DBUF
#define GUF_T GUF_ID_POOL_T
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_ONLY_TYPES
#include "guf_dbuf.h"
#ifndef GUF_ID_POOL_IMPL
typedef struct GUF_ID_POOL_NAME {
GUF_ID_POOL_DBUF free_id_dbuf;
GUF_ID_POOL_T next_id;
} GUF_ID_POOL_NAME;
#endif
#if defined(GUF_ID_POOL_IMPL) || defined(GUF_ID_POOL_IMPL_STATIC)
#define GUF_DBUF_NAME GUF_ID_POOL_DBUF
#define GUF_T GUF_ID_POOL_T
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_WITHOUT_TYPES
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _try_init)(GUF_ID_POOL_NAME *pool, ptrdiff_t initial_cap, guf_allocator *allocator, guf_err *err)
{
if (!pool) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_idpool_try_init: pool is NULL");
return NULL;
} else if (!allocator || !allocator->alloc || !allocator->realloc || !allocator->free) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_idpool_try_init: allocator is NULL");
return NULL;
} else if (initial_cap < 0) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, "in guf_idpool_try_init: initial_cap is < 0");
return NULL;
}
GUF_CAT(GUF_ID_POOL_DBUF, _try_init)(&pool->free_id_dbuf, initial_cap, allocator, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_idpool_try_init: failed to allocate dbuf");
return NULL;
}
pool->next_id = 0;
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return pool;
}
GUF_ID_POOL_T GUF_CAT(GUF_ID_POOL_NAME, _alloc_id)(GUF_ID_POOL_NAME *pool)
{
GUF_ASSERT(pool);
GUF_ASSERT(pool->free_id_dbuf.size >= 0);
if (pool->free_id_dbuf.size > 0) {
GUF_ID_POOL_T id = *GUF_CAT(GUF_ID_POOL_DBUF, _back)(&pool->free_id_dbuf);
GUF_CAT(GUF_ID_POOL_DBUF, _pop)(&pool->free_id_dbuf);
return id;
} else {
GUF_ID_POOL_T id = pool->next_id;
pool->next_id += 1; // TODO: handle overflow (need a guf_id_pool_t_max)
return id;
}
}
void GUF_CAT(GUF_ID_POOL_NAME, _try_free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id, guf_err *err)
{
GUF_ASSERT(pool);
GUF_ASSERT(pool->next_id > 0);
if (id < 0 || id >= pool->next_id) {
guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, "in guf_idpool_try_free_id: id < 0 or id >= pool->next_id");
return;
}
if (id == pool->next_id - 1) {
pool->next_id -= 1;
return;
}
GUF_CAT(GUF_ID_POOL_DBUF, _try_push_val)(&pool->free_id_dbuf, id, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_idpool_try_free_id: failed to push id");
return;
} else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
}
}
#endif /* end impl */
#undef GUF_ID_POOL_KWRDS
#undef GUF_ID_POOL_IMPL
#undef GUF_ID_POOL_IMPL_STATIC

View File

@ -51,6 +51,17 @@ GUF_DEFINE_MIN_MAX_CLAMP(double, f64)
#undef GUF_DEFINE_MIN_MAX_CLAMP #undef GUF_DEFINE_MIN_MAX_CLAMP
static inline bool guf_add_is_overflow_size_t(size_t a, size_t b)
{
return (a + b) > a;
}
static inline bool guf_mul_is_overflow_size_t(size_t a, size_t b)
{
const size_t c = a * b;
return a != 0 && ((c / a) != b);
}
static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN); return -x;} // I would not drink that... static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN); return -x;} // I would not drink that...
static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT8_MIN); return -x;} static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT8_MIN); return -x;}
static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT16_MIN); return -x;} static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT16_MIN); return -x;}

View File

@ -15,7 +15,7 @@
// cf. libc++ short-string optimisation: https://joellaity.com/2020/01/31/string.html (last-retrieved 2025-03-10) // cf. libc++ short-string optimisation: https://joellaity.com/2020/01/31/string.html (last-retrieved 2025-03-10)
typedef struct guf_str_internal_long_ { 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 capacity; // If long string: capacity's least significant bit always set to 1 (or its most significant bit for big-endian platforms); the actual capacity must be even
size_t size; size_t size;
char *c_str; char *c_str;
} guf_str_internal_long_; } guf_str_internal_long_;
@ -45,12 +45,15 @@ typedef struct guf_str {
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_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. } 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_CSTR_TO_VIEW(CSTR) ((guf_str_view){.str = (CSTR), .len = (ptrdiff_t)strlen((CSTR))})
#define GUF_CSTR_LIT_TO_VIEW(CSTR) ((guf_str_view){.str = (CSTR), .len = (ptrdiff_t)sizeof((CSTR)) - 1})
#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_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}) #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 #ifdef __cplusplus
// Standard C++ does not have compound literals like C99... // 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)} #define GUF_CSTR_TO_VIEW_CPP(CSTR) guf_str_view{.str = (CSTR), .len = (ptrdiff_t)strlen(CSTR)}
#define GUF_CSTR_LIT_TO_VIEW_CPP(CSTR) guf_str_view{.str = (CSTR), .len = (ptrdiff_t)sizeof(CSTR) - 1}
#endif #endif
// guf_str_view: // guf_str_view:
@ -72,49 +75,76 @@ GUF_STR_KWRDS int guf_str_view_cmp(const void *str_view_a, const void *str_view_
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_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);
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_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_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 void guf_str_free(guf_str *str, void *ctx);
// TODO:
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_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 guf_str guf_str_new_substr(guf_str_view str_view, ptrdiff_t pos, ptrdiff_t len, guf_allocator *alloc);
// TODO:
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 bool guf_str_equals_cstr(const guf_str *a, const char *c_str); 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 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); // DONE:
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_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_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_try_append_cstr(guf_str *str, const char *c_str, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_append_cstr(guf_str *str, const char *c_str);
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_substr(guf_str* str, size_t pos, size_t count);
// 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_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_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_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 guf_str *guf_str_shrink_to_fit(guf_str *str);
// TODO:
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); GUF_STR_KWRDS char guf_str_pop_front(guf_str *str);
// TODO:
GUF_STR_KWRDS char *guf_str_at(guf_str *str, size_t idx); 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_back(guf_str *str);
GUF_STR_KWRDS char *guf_str_front(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. // DONE:
GUF_STR_KWRDS size_t guf_str_capacity(const guf_str *str); // The capacity (in chars) without the final zero-terminator. 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_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.
GUF_STR_KWRDS ptrdiff_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_short(const guf_str *str);
GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str); GUF_STR_KWRDS bool guf_str_is_readonly(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 bool guf_str_is_uninit(const guf_str *str);
#endif #endif
// #define GUF_STR_IMPL_STATIC /* debug */ // #define GUF_STR_IMPL_STATIC /* debug */
@ -126,6 +156,7 @@ GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str);
#endif #endif
#include "guf_common.h" #include "guf_common.h"
#include "guf_math.h"
#include <string.h> #include <string.h>
#ifdef GUF_STR_IMPL #ifdef GUF_STR_IMPL
@ -142,39 +173,130 @@ GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str);
#define GUF_STR_IS_LONG_MASK ((unsigned char)1) /* binary 0000.0001 */ #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 */ #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) static inline void guf_str_set_lng_cap_(guf_str *str, size_t cap_with_null)
{ {
GUF_ASSERT(capacity % 2 == 0); GUF_ASSERT(cap_with_null % 2 == 0);
GUF_ASSERT(capacity > GUF_STR_SSO_BUF_CAP); GUF_ASSERT(cap_with_null <= PTRDIFF_MAX);
str->data.lng.capacity = capacity | ((size_t)1); GUF_ASSERT(cap_with_null > GUF_STR_SSO_BUF_CAP);
str->data.lng.capacity = cap_with_null | ((size_t)1);
} }
static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size) static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size_with_null)
{ {
GUF_ASSERT(size < GUF_STR_SSO_BUF_CAP && size < 0x80); GUF_ASSERT(size_with_null < GUF_STR_SSO_BUF_CAP && size_with_null < 0x80);
str->data.shrt.size = (unsigned char)(size << 1u); str->data.shrt.size = (unsigned char)(size_with_null << 1);
} }
#elif defined(GUF_PLATFORM_BIG_ENDIAN) #elif defined(GUF_PLATFORM_BIG_ENDIAN)
#define GUF_STR_IS_LONG_MASK ((unsigned char)0x80) /* binary 1000 0000 */ #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 */ #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) static inline void guf_str_set_lng_cap_(guf_str *str, size_t cap_with_null)
{ {
GUF_ASSERT(capacity % 2 == 0); GUF_ASSERT(cap_with_null % 2 == 0);
GUF_ASSERT(capacity > GUF_STR_SSO_BUF_CAP); GUF_ASSERT(cap_with_null <= PTRDIFF_MAX);
str->data.lng.capacity = ~GUF_STR_GET_CAP_MASK | (capacity >> 1); GUF_ASSERT(cap_with_null > GUF_STR_SSO_BUF_CAP);
str->data.lng.capacity = ~GUF_STR_GET_CAP_MASK | (cap_with_null >> 1);
} }
static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size) static inline void guf_str_set_shrt_size_(guf_str *str, unsigned char size_with_null)
{ {
GUF_ASSERT(size < GUF_STR_SSO_BUF_CAP && size < 0x80); GUF_ASSERT(size_with_null < GUF_STR_SSO_BUF_CAP && size_with_null < 0x80);
str->data.shrt.size = size; str->data.shrt.size = size_with_null;
} }
#else #else
#error "guf_str: neither GUF_PLATFORM_LITTLE_ENDIAN nor GUF_PLATFORM_BIG_ENDIAN is defined" #error "guf_str: neither GUF_PLATFORM_LITTLE_ENDIAN nor GUF_PLATFORM_BIG_ENDIAN is defined"
#endif #endif
static bool guf_str_is_valid(const guf_str *str) GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str)
{ {
GUF_ASSERT(str); GUF_ASSERT(str);
return !str->allocator;
}
static bool guf_str_is_short_internal_(const guf_str *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;
}
// Returns the capacity without the final null-terminator
static size_t guf_str_cap_internal_(const guf_str *str)
{
if (guf_str_is_short_internal_(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;
GUF_ASSERT(cap_with_null % 2 == 0);
#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;
GUF_ASSERT(cap_with_null % 2 == 0);
#endif
GUF_ASSERT(cap_with_null > 0 && cap_with_null > GUF_STR_SSO_BUF_CAP);
GUF_ASSERT(cap_with_null <= PTRDIFF_MAX);
return cap_with_null - 1;
}
}
static size_t guf_str_size_internal_(const guf_str *str)
{
if (guf_str_is_short_internal_(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;
} else {
const size_t size = str->data.lng.size;
GUF_ASSERT(size > 0 && size <= PTRDIFF_MAX);
return size;
}
}
static size_t guf_str_len_internal_(const guf_str *str)
{
const size_t size = guf_str_size_internal_(str);
GUF_ASSERT(size > 0);
if (size == 0) {
return 0;
} else {
return size - 1;
}
}
GUF_STR_KWRDS bool guf_str_is_short(const guf_str *str)
{
GUF_ASSERT(guf_str_is_valid(str));
return guf_str_is_short_internal_(str);
}
GUF_STR_KWRDS ptrdiff_t guf_str_capacity(const guf_str *str)
{
GUF_ASSERT(guf_str_is_valid(str));
return (ptrdiff_t)guf_str_cap_internal_(str);
}
GUF_STR_KWRDS ptrdiff_t guf_str_len(const guf_str *str)
{
GUF_ASSERT(guf_str_is_valid(str));
return (ptrdiff_t)guf_str_len_internal_(str);
}
GUF_STR_KWRDS bool guf_str_is_valid(const guf_str *str)
{
GUF_ASSERT(str);
if (!str || guf_str_is_uninit(str)) {
return false;
}
const bool is_readonly = !str->allocator; const bool is_readonly = !str->allocator;
if (is_readonly) { if (is_readonly) {
bool valid_readonly = str->data.lng.c_str && str->data.lng.capacity == 0 && str->data.lng.size > 0; bool valid_readonly = str->data.lng.c_str && str->data.lng.capacity == 0 && str->data.lng.size > 0;
@ -185,162 +307,413 @@ static bool guf_str_is_valid(const guf_str *str)
return false; 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). if (guf_str_is_short_internal_(str)) {
const bool is_short = (first_byte & GUF_STR_IS_LONG_MASK) == 0; const size_t size = guf_str_size_internal_(str); // len + 1
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'; 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 = str->data.lng.capacity & ~(size_t)1; const size_t cap_with_null = guf_str_cap_internal_(str) + 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; 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;
} }
} }
GUF_STR_KWRDS bool guf_str_is_readonly(const guf_str *str) 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));
return !str->allocator; GUF_ASSERT(!guf_str_is_readonly(str));
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;
if (new_cap_min <= (ptrdiff_t)old_cap_with_null) { // No need to grow.
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
} }
GUF_STR_KWRDS bool guf_str_is_short(const guf_str *str) if (new_cap_min >= PTRDIFF_MAX - 1) {
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, "in guf_str_try_reserve: new_cap_min >= PTRDIFF_MAX - 1");
return NULL;
}
size_t new_cap_min_with_null = (size_t)new_cap_min + 1;
if (new_cap_min_with_null % 2 != 0) { // Only an even lng.capacity is allowed.
new_cap_min_with_null += 1;
}
// Try if we can reach at least new_cap_min_with_null by doubling the capacity.
const size_t GUF_STR_GROWTH_FAC = 2;
size_t times_two_cap = old_cap_with_null * GUF_STR_GROWTH_FAC;
if (guf_mul_is_overflow_size_t(old_cap_with_null, GUF_STR_GROWTH_FAC) || times_two_cap >= PTRDIFF_MAX) {
times_two_cap = (PTRDIFF_MAX % 2 == 0) ? PTRDIFF_MAX : PTRDIFF_MAX - 1;
}
if (times_two_cap > new_cap_min_with_null) {
new_cap_min_with_null = times_two_cap;
}
GUF_ASSERT(new_cap_min_with_null >= len_with_null && new_cap_min_with_null <= PTRDIFF_MAX);
const size_t space_remaining = (new_cap_min_with_null - len_with_null);
if (new_cap_min_with_null < (PTRDIFF_MAX - 8) && space_remaining < 4) {
new_cap_min_with_null += 4 - space_remaining; // Have some leeway.
}
GUF_ASSERT(new_cap_min_with_null % 2 == 0);
if (guf_str_is_short_internal_(str)) { // a.) Was short string -> need initial allocation.
char *c_str_new = str->allocator->alloc(new_cap_min_with_null, 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.shrt.c_str, len_with_null);
str->data.lng.c_str = c_str_new;
guf_str_set_lng_cap_(str, new_cap_min_with_null);
} 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);
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.lng.c_str = c_str_new;
guf_str_set_lng_cap_(str, new_cap_min_with_null);
}
GUF_ASSERT(guf_str_is_valid(str));
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
static char *guf_str_get_cstr_internal_(guf_str *str)
{
if (guf_str_is_short(str)) {
return str->data.shrt.c_str;
} else {
return str->data.lng.c_str;
}
}
static const char *guf_str_get_const_cstr_internal_(const guf_str *str)
{
if (guf_str_is_short(str)) {
return str->data.shrt.c_str;
} else {
return str->data.lng.c_str;
}
}
GUF_STR_KWRDS const char *guf_str_const_cstr(const guf_str *str)
{
GUF_ASSERT(guf_str_is_valid(str));
const char* c_str = guf_str_get_const_cstr_internal_(str);
GUF_ASSERT(c_str);
return c_str;
}
GUF_STR_KWRDS char *guf_str_try_get_cstr(guf_str *str, 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)) {
return false; guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, "in guf_str_try_get_cstr: cannot return non-const char pointer because str is readonly");
return NULL;
} }
const unsigned char first_byte = str->data.shrt.size; // union type-punning (only legal in C99 and above; undefined behaviour in C++ I think). char *c_str = guf_str_get_cstr_internal_(str);
return (first_byte & GUF_STR_IS_LONG_MASK) == 0; GUF_ASSERT(c_str);
return c_str;
} }
GUF_STR_KWRDS size_t guf_str_capacity(const guf_str *str) GUF_STR_KWRDS char *guf_str_cstr(guf_str *str)
{
return guf_str_try_get_cstr(str, NULL);
}
static void guf_str_set_len_internal_(guf_str *str, size_t len)
{
GUF_ASSERT(len <= guf_str_cap_internal_(str));
GUF_ASSERT(!guf_str_is_readonly(str));
const size_t len_with_null = len + 1;
if (guf_str_is_short_internal_(str)) {
GUF_ASSERT(len_with_null <= UCHAR_MAX)
guf_str_set_shrt_size_(str, (unsigned char)len_with_null);
} else {
str->data.lng.size = len_with_null;
}
}
GUF_STR_KWRDS guf_str guf_str_new_uninitialised(void)
{
guf_str str = {.allocator = NULL, .data.shrt.size = 0, .data.shrt.c_str[0] = '\0'};
return str;
}
GUF_STR_KWRDS bool guf_str_is_uninit(const guf_str *str)
{
GUF_ASSERT(str);
return !str->allocator && !str->data.shrt.size && str->data.shrt.c_str[0] == '\0';
}
GUF_STR_KWRDS guf_str *guf_str_init_empty(guf_str *str, guf_allocator *allocator)
{
GUF_ASSERT(str && allocator);
str->allocator = allocator;
guf_str_set_shrt_size_(str, 1);
str->data.shrt.c_str[0] = '\0';
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;
}
guf_str_init_empty(str, alloc);
if (str_view.len == 0) {
GUF_ASSERT(!guf_str_is_readonly(str));
GUF_ASSERT(guf_str_is_valid(str));
return str;
}
GUF_ASSERT(str_view.str && str_view.len > 0);
guf_str_try_reserve(str, str_view.len, err);
if (err && *err != GUF_ERR_NONE) {
guf_panic(*err, "in guf_str_try_init: Initial allocation failed");
return NULL;
}
GUF_ASSERT(guf_str_len_internal_(str) == 0);
GUF_ASSERT(guf_str_cap_internal_(str) >= (size_t)str_view.len);
GUF_ASSERT(!guf_str_is_readonly(str));
char *c_str_dst = guf_str_get_cstr_internal_(str);
GUF_ASSERT_RELEASE(c_str_dst);
memcpy(c_str_dst, str_view.str, str_view.len);
c_str_dst[str_view.len] = '\0';
GUF_ASSERT(!guf_str_is_readonly(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_init(guf_str *str, guf_str_view str_view, guf_allocator *alloc)
{
return guf_str_try_init(str, str_view, alloc, NULL);
}
GUF_STR_KWRDS guf_str guf_str_try_new(guf_str_view str_view, guf_allocator *alloc, guf_err *err)
{
guf_str str = guf_str_new_uninitialised();
guf_str_try_init(&str, str_view, alloc, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_str_try_new: failed init");
return guf_str_new_uninitialised();
} else {
GUF_ASSERT(!guf_str_is_uninit(&str));
return str;
}
}
GUF_STR_KWRDS void guf_str_free(guf_str *str, void *ctx)
{
(void)ctx;
if (!str || guf_str_is_uninit(str)) {
return;
} else if (guf_str_is_readonly(str)) { // Don't need to de-allocate anything for read-only strings.
*str = guf_str_new_uninitialised();
return;
} else if (!guf_str_is_short(str)) { // Need to de-allocate.
GUF_ASSERT(guf_str_capacity(str) < PTRDIFF_MAX);
const ptrdiff_t cap_with_null = guf_str_capacity(str) + 1;
GUF_ASSERT((cap_with_null % 2) == 0);
char *c_str = guf_str_cstr(str);
GUF_ASSERT(str->allocator->free);
if (str->allocator->free) {
str->allocator->free(c_str, cap_with_null, str->allocator->ctx);
}
*str = guf_str_new_uninitialised();
return;
} else {
GUF_ASSERT(guf_str_is_short(str));
*str = guf_str_new_uninitialised();
}
}
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_short(str)) {
return GUF_STR_SSO_BUF_CAP - 1; 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");
return NULL;
}
if (times < 0) {
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, "in guf_str_try_append_char: repeats < 0");
return NULL;
} else if (times == 0) {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
GUF_ASSERT(guf_str_len_internal_(str) <= guf_str_cap_internal_(str));
const size_t old_cap = guf_str_cap_internal_(str);
const size_t old_len = guf_str_len_internal_(str);
const size_t new_len = old_len + (size_t)times;
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");
return NULL;
} else if (new_len > old_cap) { // Need to grow capacity.
guf_str_try_reserve(str, new_len, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_str_try_append_char: failed to reserve capacity");
return NULL;
}
}
const size_t new_cap = guf_str_cap_internal_(str);
GUF_ASSERT(guf_str_len_internal_(str) == old_len);
GUF_ASSERT(new_cap >= new_len && new_cap >= old_cap);
GUF_ASSERT(((ptrdiff_t)new_cap - (ptrdiff_t)old_len) >= times);
char *c_str = guf_str_get_cstr_internal_(str);
for (size_t i = old_len; i < new_len; ++i) {
c_str[i] = c;
}
guf_str_set_len_internal_(str, new_len);
c_str[new_len] = '\0';
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return str;
}
GUF_STR_KWRDS guf_str *guf_str_append_char(guf_str *str, char c, ptrdiff_t times)
{
return guf_str_try_append_char(str, c, times, NULL);
}
GUF_STR_KWRDS guf_str *guf_str_try_append_one_char(guf_str *str, char c, guf_err *err)
{
return guf_str_try_append_char(str, c, 1, err);
}
GUF_STR_KWRDS guf_str *guf_str_append_one_char(guf_str *str, char c)
{
return guf_str_try_append_one_char(str, c, NULL);
}
GUF_STR_KWRDS guf_str *guf_str_try_append(guf_str *str, guf_str_view sv, guf_err *err)
{
GUF_ASSERT(guf_str_is_valid(str));
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");
return NULL;
} else if (guf_str_is_readonly(str)) { } else if (guf_str_is_readonly(str)) {
return 0; guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in in guf_str_try_append_view: str is readonly");
} else { return NULL;
// 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. if (sv.len == 0) {
const size_t cap_with_null = str->data.lng.capacity & GUF_STR_GET_CAP_MASK; guf_err_set_if_not_null(err, GUF_ERR_NONE);
#elif defined(GUF_PLATFORM_BIG_ENDIAN) return str;
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; GUF_ASSERT(sv.str && sv.len > 0);
#endif
GUF_ASSERT(cap_with_null > 0 && cap_with_null > GUF_STR_SSO_BUF_CAP); const size_t old_cap = guf_str_cap_internal_(str);
return cap_with_null - 1; const size_t old_len = guf_str_len_internal_(str);
const size_t new_len = old_len + (size_t)sv.len;
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");
return NULL;
} else if (new_len > old_cap) { // Growth necessary.
guf_str_try_reserve(str, new_len, err);
if (err && *err != GUF_ERR_NONE) {
guf_err_set_or_panic(err, *err, "in guf_str_try_append_view: failed to reserve capacity");
return NULL;
} }
} }
GUF_STR_KWRDS size_t guf_str_len(const guf_str *str) const size_t new_cap = guf_str_cap_internal_(str);
GUF_ASSERT(new_cap >= old_cap && new_cap >= new_len);
GUF_ASSERT(((ptrdiff_t)new_cap - (ptrdiff_t)old_len) >= sv.len);
char *c_str_dst = guf_str_get_cstr_internal_(str);
for (size_t dst_i = old_len, src_i = 0; dst_i < new_len; ++dst_i, ++src_i) {
GUF_ASSERT(src_i < (size_t)sv.len);
c_str_dst[dst_i] = sv.str[src_i];
}
c_str_dst[new_len] = '\0';
guf_str_set_len_internal_(str, new_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_append(guf_str *str, guf_str_view sv)
{
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)); GUF_ASSERT(guf_str_is_valid(str));
if (guf_str_is_short(str)) {
GUF_ASSERT(str->data.shrt.size > 0); if (!c_str) {
#if defined(GUF_PLATFORM_LITTLE_ENDIAN) guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_append_cstr: c_str is NULL");
const size_t size = (str->data.shrt.size >> 1); return NULL;
#elif defined(GUF_PLATFORM_BIG_ENDIAN) } else if (guf_str_is_readonly(str)) {
const size_t size = (str->data.shrt.size); guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, "in guf_str_try_append_cstr: str is readonly");
#endif return NULL;
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) char *dst_cstr = guf_str_get_cstr_internal_(str);
// { size_t i = 0;
// GUF_ASSERT_RELEASE(guf_str_is_valid_writable(str)); do {
size_t cap = guf_str_cap_internal_(str);
size_t len = guf_str_len_internal_(str);
GUF_ASSERT(len <= cap);
// const ptrdiff_t size = guf_str_len(str) + 1; if (len == cap) { // Grow if necessary.
// const ptrdiff_t old_cap = guf_str_capacity(str); guf_str_try_reserve(str, cap < PTRDIFF_MAX ? cap + 1 : PTRDIFF_MAX, 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);
}
// if (new_cap <= old_cap) { // Growth not necessary. dst_cstr[len] = c_str[i];
// guf_err_set_if_not_null(err, GUF_ERR_NONE); guf_str_set_len_internal_(str, len + 1);
// return str; } while (c_str[i++] != '\0');
// }
// if (guf_str_is_short(str)) { // a.) Was short string -> need initial allocation. GUF_ASSERT(guf_str_is_valid(str));
// GUF_ASSERT(size == GUF_STR_SSO_BUFCAP); GUF_ASSERT(dst_cstr[guf_str_len_internal_(str)] == '\0');
// 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_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) GUF_STR_KWRDS guf_str *guf_str_append_cstr(guf_str *str, const char *c_str)
// { {
// if (!str) { return guf_str_try_append_cstr(str, c_str, NULL);
// 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;
// }

View File

@ -1,10 +1,36 @@
- guf_stack, guf_queue, guf_ringbuf - sort: add cpp #ifdef to remove restrict from declaration
- guf_stack, guf_queue, guf_dqueue, guf_prio_queue (using a heap), guf_ringbuf
- 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?
- potential idea for alloc: instead of using a pointer (8 or 4 bytes), use a 2 byte id to save space (and avoid dangling pointers):
typedef guf_allocator_id uint16_t; // allows for up to (UINT16_MAX - 1) allocators (and 0 for NULL)
guf_allocator_dbuf guf_global_allocators;
// cf. https://github.com/erincatto/box2d/blob/main/src/id_pool.c
typedef struct guf_allocator_id_pool { // Implement generically with macro "templates"
guf_allocator_id_dbuf free_ids;
guf_allocator_id next_free_id;
} guf_allocator_id_pool;
guf_allocator_id_pool guf_global_allocator_id_pool;
guf_allocator_id guf_register_allocator(guf_allocator allocator) {
guf_allocator_id id = get_free_id(&guf_global_allocator_id_pool);
if (!id) {
return 0;
}
guf_global_allocators[id] = allocator;
}
guf_allocator *guf_get_allocator(guf_allocator_id id) {
return (id == 0 || id >= UINT16_MAX) ? NULL : (guf_global_allocators + id);
}
- no guf_init.h - no guf_init.h
- linalg: float precision question += elem * -val / pivot_val vs elem * (-val / pivot_val) - linalg: float precision question += elem * -val / pivot_val vs elem * (-val / pivot_val)
- unicode normalisation - unicode normalisation
- track allocs for test (implement alloc tracker)
- handle right-to-left text properly - handle right-to-left text properly
- fix 32-bit dict (and add 32/64 bit defs and 32/64-bit platform detection in common.h)