#include <stdlib.h>
#include <string.h>

#include "buffer.h"


Buffer *buffer_new() {

    Buffer *buf = calloc(1, sizeof(Buffer));
    return buf;

}

void buffer_delete(Buffer *buf) {

    if (buf->array) {
        free(buf->array);
    }

    free(buf);

}

void buffer_init(Buffer *buf) {

    memset(buf, 0, sizeof(Buffer));

}

void buffer_clear(Buffer *buf) {

    if (buf->array) {
        free(buf->array);
    }

    buffer_init(buf);

}

void buffer_grow(Buffer *buf, size_t n) {

    if (n == 0) {
        n = BUFFER_DEFAULT_CAPACITY;
    }

    if (n > buf->capacity) {
        buf->array = realloc(buf->array, n);
        buf->capacity = n;
    }

}

void buffer_reserve(Buffer *buf, size_t n) {

    size_t cap = buf->capacity;
    size_t end = buf->end;

    if (cap - end < n) {
        do { cap <<= 1; } while (cap - end < n);
        buffer_grow(buf, cap);
    }

}

void buffer_put(Buffer *buf, const void *src, size_t n) {

    memcpy(buf->array + buf->end, src, n);
    buf->end += n;

}

size_t buffer_get(Buffer *buf, void *dst, size_t n) {

    size_t size = buffer_size(buf);

    if (n > size) {
        n = size;
    }

    memcpy(dst, buf->array + buf->start, n);
    buffer_skip(buf, n);

    return n;

}

void buffer_skip(Buffer *buf, size_t n) {

    size_t size = buffer_size(buf);

    if (n < size) {
        buf->start += n;
    }
    else {
        buffer_reset(buf);
    }

}

void buffer_pack(Buffer *buf) {

    if (buf->start == 0) {
        return;  // already packed
    }

    size_t size = buffer_size(buf);

    if (size) {
        memmove(buf->array, buf->array + buf->start, size);
        buf->start = 0;
        buf->end = size;
    }
    else {
        buffer_reset(buf);
    }

}
