summaryrefslogtreecommitdiffstats
path: root/include/buffer.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/buffer.h')
-rw-r--r--include/buffer.h232
1 files changed, 232 insertions, 0 deletions
diff --git a/include/buffer.h b/include/buffer.h
new file mode 100644
index 0000000..2365173
--- /dev/null
+++ b/include/buffer.h
@@ -0,0 +1,232 @@
+// buffer.h - Dynamic buffer with metadata
+//
+// Thread safety: Not thread-safe. External synchronization required.
+// Memory safety: NULL-safe on all operations. buffer_resize returns
+// original pointer on failure; use buffer_resize_ok for NULL on failure.
+// Resize policy: doubles when usage > 80%, halves when < 20% (min 32).
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include "type.h"
+
+typedef struct {
+ u64 n; // Array capacity
+ u64 i; // Element count
+ u64 s; // Element size
+} buffer_meta_t;
+
+#define _buffer_from_meta(x) ((void *)((u8 *)(x) + sizeof(buffer_meta_t)))
+#define _buffer_to_meta(x) ((void *)((u8 *)(x) - sizeof(buffer_meta_t)))
+
+void *buffer_malloc(u64 num, u64 size);
+void buffer_free(void *);
+void *buffer_resize(void *);
+void *buffer_resize_ok(void *);
+u64 buffer_count(const void *);
+u64 buffer_byte_count(const void *);
+u64 buffer_capacity(const void *);
+u64 buffer_element_size(const void *);
+
+#endif // BUFFER_H
+
+#ifdef BUFFER_IMPLEMENTATION
+#ifndef BUFFER_IMPLEMENTED
+#define BUFFER_IMPLEMENTED
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// buffer_malloc()
+//
+// Allocate a dynamic buffer with embedded metadata
+//
+// n - Initial element capacity
+// s - Element size in bytes
+//
+// return - Pointer to data region, or NULL on failure
+
+void *buffer_malloc(u64 n, u64 s) {
+ if (n == 0 || s == 0)
+ return NULL;
+ if (n > (SIZE_MAX - sizeof(buffer_meta_t)) / s)
+ return NULL;
+
+ void *x = malloc((n * s) + sizeof(buffer_meta_t));
+ if (!x)
+ return NULL;
+
+ buffer_meta_t *meta = (buffer_meta_t *)x;
+ meta->i = 0;
+ meta->n = n;
+ meta->s = s;
+
+ return _buffer_from_meta(x);
+}
+
+// buffer_free()
+//
+// Release a dynamic buffer and its metadata
+//
+// buffer - Pointer returned by buffer_malloc
+
+void buffer_free(void *buffer) {
+ if (buffer == NULL)
+ return;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ meta->n = 0;
+ meta->i = 0;
+ meta->s = 0;
+ free(_buffer_to_meta(buffer));
+}
+
+// buffer_count()
+//
+// Return the number of elements currently in use
+//
+// buffer - Pointer to buffer data region
+//
+// return - Element count, or 0 if buffer is NULL
+
+u64 buffer_count(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ return meta->i;
+}
+
+// buffer_byte_count()
+//
+// Return the number of bytes currently in use
+//
+// buffer - Pointer to buffer data region
+//
+// return - Byte count (count * element size), or 0 if buffer is NULL
+
+u64 buffer_byte_count(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ if (meta->s == 0 || meta->i > UINT64_MAX / meta->s)
+ return 0;
+ return meta->i * meta->s;
+}
+
+// buffer_element_size()
+//
+// Return the size of each element in bytes
+//
+// buffer - Pointer to buffer data region
+//
+// return - Element size, or 0 if buffer is NULL
+
+u64 buffer_element_size(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ return meta->s;
+}
+
+// buffer_capacity()
+//
+// Return the total element capacity
+//
+// buffer - Pointer to buffer data region
+//
+// return - Capacity, or 0 if buffer is NULL
+
+u64 buffer_capacity(const void *buffer) {
+ if (!buffer)
+ return 0;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ return meta->n;
+}
+
+// buffer_resize()
+//
+// Automatically resize buffer based on usage ratio. Doubles capacity when
+// usage > 80%, halves when < 20% (min 32). Returns original pointer on
+// realloc failure — use buffer_resize_ok for NULL on failure.
+//
+// buffer - Pointer to buffer data region
+//
+// return - New data pointer (may differ from input), or original on failure
+
+void *buffer_resize(void *buffer) {
+ if (!buffer)
+ return NULL;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ u64 new_n = meta->n;
+ if (meta->n == 0)
+ return buffer;
+ f64 ratio = (f64)meta->i / (f64)meta->n;
+
+ if (ratio > 0.8) {
+ if (new_n > UINT64_MAX / 2)
+ return buffer;
+ new_n *= 2;
+ } else if (ratio < 0.2 && new_n > 32) {
+ new_n /= 2;
+ }
+
+ if (new_n == meta->n)
+ return buffer;
+
+ if (meta->s == 0 || new_n > (SIZE_MAX - sizeof(buffer_meta_t)) / meta->s)
+ return buffer;
+
+ void *new_meta = realloc(meta, sizeof(buffer_meta_t) + new_n * meta->s);
+ if (!new_meta)
+ return buffer;
+
+ meta = (buffer_meta_t *)new_meta;
+ meta->n = new_n;
+
+ return _buffer_from_meta(new_meta);
+}
+
+// buffer_resize_ok()
+//
+// Same as buffer_resize, but returns NULL on realloc failure instead of
+// the original pointer. Caller must check return value.
+//
+// buffer - Pointer to buffer data region
+//
+// return - New data pointer, original if no resize needed, or NULL on failure
+
+void *buffer_resize_ok(void *buffer) {
+ if (!buffer)
+ return NULL;
+ buffer_meta_t *meta = (buffer_meta_t *)_buffer_to_meta(buffer);
+ u64 old_n = meta->n;
+ u64 new_n = meta->n;
+ if (meta->n == 0)
+ return buffer;
+ f64 ratio = (f64)meta->i / (f64)meta->n;
+
+ if (ratio > 0.8) {
+ if (new_n > UINT64_MAX / 2)
+ return buffer;
+ new_n *= 2;
+ } else if (ratio < 0.2 && new_n > 32) {
+ new_n /= 2;
+ }
+
+ if (new_n == old_n)
+ return buffer;
+
+ if (meta->s == 0 || new_n > (SIZE_MAX - sizeof(buffer_meta_t)) / meta->s)
+ return buffer;
+
+ void *new_meta = realloc(meta, sizeof(buffer_meta_t) + new_n * meta->s);
+ if (!new_meta)
+ return NULL;
+
+ meta = (buffer_meta_t *)new_meta;
+ meta->n = new_n;
+
+ return _buffer_from_meta(meta);
+}
+
+#endif /* BUFFER_IMPLEMENTED */
+#endif // BUFFER_IMPLEMENTATION