// 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 #include // 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