sic

The sic programming language, compiler and tools (WIP)
Log | Files | Refs

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;
}