commit 49885c39ef3d0b4067d7e4532e4816f79ed42c59
parent f57583bece1d460cec1a66c374c9651321a28113
Author: keyle <keyle@capsule.org>
Date: Thu, 25 Jun 2026 23:04:25 +1000
early buffer string pool impl
Diffstat:
| A | src/buffer.c | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/buffer.h | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 174 insertions(+), 0 deletions(-)
diff --git a/src/buffer.c b/src/buffer.c
@@ -0,0 +1,127 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include "buffer.h"
+
+#define BUFFER_SIZE (8 * 1024 * 1024)
+
+typedef struct buffer_block {
+ struct buffer_block *next;
+ size_t len;
+ size_t cap;
+ char data[];
+};
+
+struct buffer_block *
+buf_new_block(size_t mincap) {
+ size_t cap = BUFFER_SIZE;
+ struct buffer_block *b;
+
+ if (cap < mincap) { cap = mincap; }
+
+ if (cap > SIZE_MAX - sizeof(struct buffer_block)) {
+ fprintf(stderr, "buffer_block: size overflow\n");
+ exit(1);
+ }
+
+ b = malloc(sizeof(struct buffer_block) + cap);
+ if (!b) {
+ fprintf(stderr, "buffer_block: could not allocate %zu bytes\n", cap);
+ exit(1);
+ }
+
+ b->next = NULL;
+ b->len = 0;
+ b->cap = cap;
+
+ return b;
+}
+
+void *
+buf_pool_alloc(struct buffer *buf, size_t size) {
+ struct buffer_block *b;
+ void *ptr;
+
+ if (!buf->current) {
+ buf->head = buf->current = buf_new_block(size);
+ }
+
+ if (size > buf->current->cap - buf->current->len) {
+ b = buf_new_block(size);
+ buf->current->next = b;
+ buf->current = b;
+ }
+
+ ptr = buf->current->data + buf->current->len;
+ buf->current->len += size; // TODO fine for string but needs to be aligned if used for structs.
+
+ return ptr;
+}
+
+char *
+buf_strn(struct buffer *buf, char *s, size_t len) {
+ char *dst;
+ if (len == SIZE_MAX) {
+ fprintf(stderr, "buffer_block: too large\n");
+ exit(1);
+ }
+
+ dst = buf_pool_alloc(buf, len + 1);
+ memcpy(dst, s, len);
+ dst[len] = '\0';
+
+ return dst;
+}
+
+char *
+buf_str(struct buffer *buf, char *s) {
+ return buf_strn(buf, s, strlen(s));
+}
+
+char *
+buf_vfmt(struct buffer *buf, const char *fmt, va_list args) {
+ va_list copy;
+ int needed;
+ char *dst;
+ va_copy(copy, args);
+
+ needed = vsnprintf(NULL, 0, fmt, copy);
+ va_end(copy);
+ if (needed < 0) {
+ fprintf(stderr, "fmt: formatting failed\n");
+ exit(1);
+ }
+
+ dst = buf_pool_alloc(buf, (size_t)needed + 1);
+ vsnprintf(dst, (size_t)needed + 1, fmt, args);
+
+ return dst;
+}
+
+void
+buf_reset(struct buffer *buf) {
+ struct buffer_block *b;
+
+ for (b = buf->head; b; b = b->next) {
+ b->len = 0;
+ }
+
+ buf->current = buf->head;
+}
+
+void
+buf_free_all(struct buffer *buf) {
+ struct buffer_block *b = buf->head;
+ struct buffer_block *next_b;
+
+ while (b) {
+ next_b = b->next;
+ free(b);
+ b = next_b;
+ }
+
+ buf->head = NULL;
+ buf->current = NULL;
+}
diff --git a/src/buffer.h b/src/buffer.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <stdarg.h>
+#include <stddef.h>
+
+// note utf-16 currently not supported
+
+typedef struct buffer_block Buffer_Block;
+
+typedef struct buffer {
+ Buffer_Block *head;
+ Buffer_Block *current;
+} Buffer;
+
+void *buf_pool_alloc(struct buffer *buf, size_t size);
+char *buf_strn(struct buffer *buf, char *s, size_t len);
+char *buf_str(struct buffer *buf, char *s);
+char *buf_vfmt(struct buffer *buf, const char *fmt, va_list args);
+void buf_reset(struct buffer *buf);
+void buf_free_all(struct buffer *buf);
+
+#define BUFFER_DEFINE(prefix) \
+ static Buffer prefix##_buffer = {0}; \
+ \
+ static inline char *prefix##_strn(char *s, size_t len) { \
+ return buf_strn(&prefix##_buffer, s, len); \
+ } \
+ \
+ static inline char *prefix##_str(char *s) { \
+ return buf_str(&prefix##_buffer, s); \
+ } \
+ \
+ static inline char *prefix##_fmt(char *fmt, ...) { \
+ va_list args; \
+ va_start(args, fmt); \
+ char *result = buf_vfmt(&prefix##_buffer, fmt, args); \
+ va_end(args); \
+ return result; \
+ } \
+ \
+ static inline void prefix##_reset(void) { \
+ buf_reset(&prefix##_buffer); \
+ } \
+ \
+ static inline void prefix##_free_all(void) { \
+ buf_free_all(&prefix##_buffer); \
+ }