#ifndef GUF_STR_H #define GUF_STR_H #include #include #include "guf_common.h" #include "guf_obj.h" #define GUF_STR_ABORT_ON_ALLOC_FAILURE 1 // TODO: don't allocate self but take allocator? typedef enum guf_str_state { GUF_STR_STATE_INIT = 0, GUF_STR_STATE_SHORT = 1, GUF_STR_STATE_ALLOC_ERR = 2 } guf_str_state; typedef struct guf_str { guf_str_state state; union { struct heap { char *c_str; size_t len, capacity; // len and capacity do not include the null-terminator. } heap; struct stack { // Short-string optimisation. #define GUF_STR_SSO_BUFSIZE (sizeof(struct heap) - sizeof(unsigned char)) #define GUF_STR_SSO_BUFCAP (GUF_STR_SSO_BUFSIZE - 1) char c_str[GUF_STR_SSO_BUFSIZE]; unsigned char len; } stack; } data; } guf_str; typedef struct guf_str_view { const char *str; size_t len; } guf_str_view; #define GUF_CSTR_TO_VIEW(C_STR_PTR) ((guf_str_view){.str = (C_STR_PTR), .len = strlen((C_STR_PTR))}) #define GUF_STR_TO_VIEW(GUF_STR_PTR) ((guf_str_view){.str = guf_str_const_cstr((GUF_STR_PTR)), .len = guf_str_len((GUF_STR_PTR))}) extern const guf_str GUF_STR_UNINITIALISED; extern const guf_str GUF_STR_UNINITIALISED_FAILED_ALLOC; // TODO: find_first_of and tokenise -> for parsing, see aoclib. // Creation: guf_str *guf_str_init(guf_str *str, guf_str_view str_view); guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str); guf_str *guf_str_init_empty_with_capacity(guf_str *str, size_t capacity); // guf_str_new functions return GUF_DICT_UNINITIALISED or GUF_STR_UNINITIALISED_FAILED_ALLOC on failure (can be checked with guf_str_alloc_success) guf_str guf_str_new(guf_str_view str_view); guf_str guf_str_new_from_cstr(const char *c_str); guf_str guf_str_new_empty_with_capacity(size_t capacity); // Destruction: void guf_str_free(guf_str *str); // Modification: guf_str *guf_str_append(guf_str *str, guf_str_view to_append); guf_str *guf_str_append_cstr(guf_str *str, const char *cstr_to_append); guf_str *guf_str_substr(guf_str* str, size_t pos, size_t count); guf_str *guf_str_reserve(guf_str *str, size_t bufsize); guf_str *guf_str_shrink_capacity(guf_str *str, size_t shrink_trigger_fac, bool shrink_exact); char guf_str_pop_back(guf_str *str); char guf_str_pop_front(guf_str *str); // Copying and viewing: guf_str guf_str_substr_cpy(guf_str_view str, size_t pos, size_t count); guf_str_view guf_str_substr_view(guf_str_view str, size_t pos, size_t count); // Indexing: char *guf_str_at(guf_str *str, size_t idx); char *guf_str_back(guf_str *str); char *guf_str_front(guf_str *str); const char *guf_str_const_cstr(const guf_str *str); // Metadata retrieval: size_t guf_str_len(const guf_str *str); // The size (in chars) without the final zero-terminator (size - 1). size_t guf_str_capacity(const guf_str *str); bool guf_str_is_stack_allocated(const guf_str *str); bool guf_str_is_valid(const guf_str *str); bool guf_str_alloc_success(const guf_str *str); // Comparison: bool guf_str_view_equal(guf_str_view a, guf_str_view b); bool guf_str_equal(const guf_str *a, const guf_str *b); bool guf_str_equals_cstr(const guf_str *a, const char *c_str); bool guf_str_equals_strview(const guf_str *a, guf_str_view b); int guf_str_view_cmp(const void *str_view_a, const void *str_view_b); // For qsort etc. // UTF-8 operations. bool guf_str_char_is_ascii(char c); bool guf_str_is_ascii(const guf_str *str); // TODO: // typedef struct guf_str_pool_elem { // guf_str str; // bool in_use; // struct guf_str_pool_elem *next_free; // } guf_str_pool_elem; // typedef struct guf_str_pool { // guf_str_pool_elem *elems; // size_t capacity, num_in_use; // size_t min_str_bufsize; // guf_str_pool_elem *first_free; // } guf_str_pool; // void guf_str_pool_init(guf_str_pool *pool, size_t capacity, size_t str_initial_size) // { // if (capacity < 1) { // capacity = 1; // } // pool->num_in_use = 0; // pool->capacity = capacity; // pool->elems = calloc(capacity, sizeof(guf_str_pool_elem)); // pool->min_str_bufsize = str_initial_size; // GUF_ASSERT_RELEASE(pool->elems); // pool->first_free = pool->elems + 0; // for (size_t i = 0; i < pool->capacity; ++i) { // pool->elems[i].in_use = false; // pool->elems[i].str = guf_str_new_empty_with_capacity(pool->min_str_bufsize); // pool->elems[i].next_free = i + 1 < pool->capacity ? pool->elems + i + 1 : NULL; // } // } // find_free and find_free_with_cap #endif