diff options
Diffstat (limited to 'include/buffer.h')
| -rw-r--r-- | include/buffer.h | 232 |
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 |