buffer.c (2534B)
#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;
}