From: cassowarii Date: Wed, 24 Jun 2026 01:22:15 +0000 (-0700) Subject: add string object storage (can't free yet) X-Git-Url: https://git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=021a517c65abdba5996fcaf5fb0d76f23a130826;p=sarabande.git add string object storage (can't free yet) --- diff --git a/src/data/string.c b/src/data/string.c new file mode 100644 index 0000000..c34736a --- /dev/null +++ b/src/data/string.c @@ -0,0 +1,199 @@ +#include "data/string.h" + +#include "mem/mem.h" +#include + +#define INLINE_BUFFER_SIZE 256 +#define STRINGS_PER_BLOCK 256 + +/* TODO: Move these globals into some kind of "execution context" struct + * that we can pass around to be reentrant */ +/* also TODO a better (slightly more annoying to code) way to structure + * this would be SoA style: instead of storing free_count and allocated + * inside the block and entry respectively, store them in a separate + * array next to the pointers that we can scan quickly. less important + * for allocated since it's already in the strblk anyway, but for free_count + * this could be important because the counts will otherwise be scattered + * to the four winds */ +sbBuffer g_string_blocks; +usize g_free_block_index; + +typedef struct strentry { + usize length; + u64 handle; + flag is_external; + flag allocated; + union { + char inline_value[INLINE_BUFFER_SIZE]; + sbBuffer somewhere_else_value; + }; +} strentry; + +typedef struct strblk { + usize id; + usize free_count; + usize next_free_index; + strentry entries[STRINGS_PER_BLOCK]; +} strblk; + +static const char *get_ptr_of_entry(strentry *e); +static strblk *get_strblk(usize index); +static strentry *get_entry(strblk *blk, usize index); +static strentry *new_entry(usize length); +static void place_str_at_offset(strentry *e, usize offset, const char *str, usize length); + +void sbString_sys_init() { + g_free_block_index = 0; + sbBuffer_initialize(&g_string_blocks, sizeof(strblk*) * 8); +} + +void sbString_sys_deinit() { + sbBuffer_deinitialize(&g_string_blocks); +} + +hString sbString_alloc(const char *value, usize length) { + strentry *e = new_entry(length); + place_str_at_offset(e, 0, value, length); + return (hString)e->handle; +} + +const char *sbString_get_ptr(hString handle, usize *length_out) { + strblk *blk = get_strblk(handle / STRINGS_PER_BLOCK); + strentry *entry = get_entry(blk, handle % STRINGS_PER_BLOCK); + if (length_out) *length_out = entry->length; + return get_ptr_of_entry(entry); +} + +/* --- */ + +static const char *get_ptr_of_entry(strentry *e) { + if (e->is_external) { + return e->somewhere_else_value.data; + } else { + return e->inline_value; + } +} + +static char *get_mutable_ptr_of_entry(strentry *e) { + /* TODO: When we have static strings, panic here if we try to get + * a mutable ptr to one of them. (Should copy instead first.) */ + if (e->is_external) { + return e->somewhere_else_value.data; + } else { + return e->inline_value; + } +} + +static strblk *get_strblk(usize index) { + usize nblocks = g_string_blocks.size / sizeof(strblk*); + if (index >= nblocks) PANIC("attempt to get a string from a block (block #%zu) that does not exist!", index); + + return ((strblk**)g_string_blocks.data)[index]; +} + +static strentry *get_entry(strblk *blk, usize index) { + if (index >= STRINGS_PER_BLOCK) PANIC("attempt to get a string (string #%zu) that does not exist in block at %zu!", index, blk->id); + + return &blk->entries[index]; +} + +static strblk *new_strblk() { + usize nblocks = g_string_blocks.size / sizeof(strblk*); + strblk *new_block = calloc(1, sizeof(strblk)); + new_block->id = nblocks; + strblk **where_to_put = sbBuffer_expand(&g_string_blocks, sizeof(strblk*)); + *where_to_put = new_block; + return new_block; +} + +static strentry *alloc_entry(strblk *blk) { + if (blk->free_count == 0) PANIC("attempt to allocate from a full strblk (%zu)!", blk->id); + + blk->free_count --; + + usize free_index = blk->next_free_index; + + if (blk->free_count > 0) { + usize next_free_index = blk->next_free_index; + do { + next_free_index ++; + next_free_index %= STRINGS_PER_BLOCK; + } while (blk->entries[free_index].allocated && next_free_index != blk->next_free_index); + + if (next_free_index == blk->next_free_index) { + PANIC("free count lied! for strblk #%zu", blk->id); + } + + blk->next_free_index = next_free_index; + } + + blk->entries[free_index].allocated = TRUE; + + return &blk->entries[free_index]; +} + +static strblk *find_free_block() { + strblk *block_with_free = get_strblk(g_free_block_index); + + if (block_with_free->free_count == 0) { + usize nblocks = g_string_blocks.size / sizeof(strblk*); + usize start_index = g_free_block_index; + + do { + g_free_block_index ++; + g_free_block_index %= nblocks; + block_with_free = get_strblk(g_free_block_index); + } while (g_free_block_index != start_index && block_with_free->free_count == 0); + + if (g_free_block_index == start_index) { + /* Where did that bring you? Back to me. */ + block_with_free = new_strblk(); + g_free_block_index = nblocks; + } + } + + return block_with_free; +} + +static void set_entry_size(strentry *e, usize length) { + if (e->is_external) { + sbBuffer_set_size(&e->somewhere_else_value, length + 1); + /* if we shortened string, need to replace NUL at end */ + e->somewhere_else_value.data[length] = '\0'; + } else { + if (length > INLINE_BUFFER_SIZE - 1) { + /* new length is too big for inline, so we need to copy to the heap */ + sbBuffer new_buf; + /* length + 1 for NUL terminator. even though we track the length, it's + * easier to just preserve NUL terminator for talking to C lib functions */ + sbBuffer_initialize(&new_buf, length + 1); + if (e->length > 0) { + sbBuffer_append(&new_buf, e->inline_value, e->length + 1); + } + e->is_external = TRUE; + e->somewhere_else_value = new_buf; + e->somewhere_else_value.data[length] = '\0'; + } else { + /* we can remain inline, but need to set NUL in the right place */ + e->inline_value[length] = '\0'; + } + } +} + +static void place_str_at_offset(strentry *e, usize offset, const char *str, usize length) { + if (offset >= e->length) return; /* no room */ + + usize max_bytes_to_write = e->length - offset; + if (length < max_bytes_to_write) max_bytes_to_write = length; + + char *where_to_put = get_mutable_ptr_of_entry(e) + offset; + memcpy(where_to_put, str, max_bytes_to_write); +} + +static strentry *new_entry(usize length) { + strblk *new_block = find_free_block(); + strentry *new_entry = alloc_entry(new_block); + new_entry->handle = new_block->id * STRINGS_PER_BLOCK + (new_entry - new_block->entries) / sizeof(strentry); + set_entry_size(new_entry, length); + return new_entry; +} diff --git a/src/data/string.h b/src/data/string.h new file mode 100644 index 0000000..8ae25fc --- /dev/null +++ b/src/data/string.h @@ -0,0 +1,12 @@ +#include "common.h" + +typedef const u64 hString; + +hString sbString_alloc(const char *value, usize length); + +const char *sbString_get_ptr(hString string, usize *length_out); + +void sbString_sys_init(); + +void sbString_sys_deinit(); + diff --git a/src/global.h b/src/global.h index b12ad0d..8bfc379 100644 --- a/src/global.h +++ b/src/global.h @@ -2,9 +2,9 @@ #define __SB_GLOBAL_H__ #ifdef DEBUG -#define PANIC(msg) do { fprintf(stderr, "PANIC: " msg " at " __FILE__ ":%d\n", __LINE__); abort(); } while (0) +#define PANIC(...) do { fprintf(stderr, "PANIC: " __VA_ARGS__); fprintf(stderr, "\n at " __FILE__ ":%d\n", __LINE__); abort(); } while (0) #else -#define PANIC(msg) do { fprintf(stderr, "PANIC: " msg "\n"); abort(); } while (0) +#define PANIC(...) do { fprintf(stderr, "PANIC: " __VA_ARGS__); fprintf(stderr, "\n"); abort(); } while (0) #endif #include @@ -24,4 +24,7 @@ typedef uint8_t flag; typedef size_t usize; typedef ptrdiff_t isize; +#define TRUE ((flag)1) +#define FALSE ((flag)0) + #endif diff --git a/src/mem/buffer.c b/src/mem/buffer.c index c474aea..faa7d4a 100644 --- a/src/mem/buffer.c +++ b/src/mem/buffer.c @@ -64,6 +64,14 @@ void *sbBuffer_shrink(hBuffer buf, usize shrink_size) { return &buf->data[buf->size]; } +void sbBuffer_set_size(hBuffer buf, usize new_size) { + if (new_size > buf->size) { + sbBuffer_expand(buf, new_size - buf->size); + } else if (new_size < buf->size) { + sbBuffer_shrink(buf, buf->size - new_size); + } +} + void sbBuffer_append(hBuffer buf, const char *data, usize data_length) { if (data_length == 0) return; diff --git a/src/mem/buffer.h b/src/mem/buffer.h index f04347c..949b94e 100644 --- a/src/mem/buffer.h +++ b/src/mem/buffer.h @@ -21,6 +21,8 @@ void *sbBuffer_expand(hBuffer buf, usize expand_size); void *sbBuffer_shrink(hBuffer buf, usize shrink_size); +void sbBuffer_set_size(hBuffer buf, usize new_size); + void sbBuffer_append(hBuffer buf, const char *data, usize data_length); void sbBuffer_reset(hBuffer buf); diff --git a/src/mem/sbstring.c b/src/mem/cstrutil.c similarity index 97% rename from src/mem/sbstring.c rename to src/mem/cstrutil.c index 82f39dd..88d940d 100644 --- a/src/mem/sbstring.c +++ b/src/mem/cstrutil.c @@ -1,4 +1,4 @@ -#include "sbstring.h" +#include "cstrutil.h" usize sbstrncpy(char *dst, const char *src, usize limit) { usize count = 0; diff --git a/src/mem/sbstring.h b/src/mem/cstrutil.h similarity index 100% rename from src/mem/sbstring.h rename to src/mem/cstrutil.h diff --git a/src/mem/mem.h b/src/mem/mem.h index 85a6f10..1498f62 100644 --- a/src/mem/mem.h +++ b/src/mem/mem.h @@ -4,6 +4,6 @@ #include "global.h" #include "arena.h" #include "buffer.h" -#include "sbstring.h" +#include "cstrutil.h" #endif