struct bytebuffer {

char *buf;
int len;
int cap;

};

static void bytebuffer_reserve(struct bytebuffer *b, int cap) {

if (b->cap >= cap) {
        return;
}

// prefer doubling capacity
if (b->cap * 2 >= cap) {
        cap = b->cap * 2;
}

char *newbuf = malloc(cap);
if (b->len > 0) {
        // copy what was there, b->len > 0 assumes b->buf != null
        memcpy(newbuf, b->buf, b->len);
}
if (b->buf) {
        // in case there was an allocated buffer, free it
        free(b->buf);
}
b->buf = newbuf;
b->cap = cap;

}

static void bytebuffer_init(struct bytebuffer *b, int cap) {

b->cap = 0;
b->len = 0;
b->buf = 0;

if (cap > 0) {
        b->cap = cap;
        b->buf = malloc(cap); // just assume malloc works always
}

}

static void bytebuffer_free(struct bytebuffer *b) {

if (b->buf)
        free(b->buf);

}

static void bytebuffer_clear(struct bytebuffer *b) {

b->len = 0;

}

static void bytebuffer_append(struct bytebuffer *b, const char *data, int len) {

bytebuffer_reserve(b, b->len + len);
memcpy(b->buf + b->len, data, len);
b->len += len;

}

static void bytebuffer_puts(struct bytebuffer *b, const char *str) {

bytebuffer_append(b, str, strlen(str));

}

static void bytebuffer_resize(struct bytebuffer *b, int len) {

bytebuffer_reserve(b, len);
b->len = len;

}

static void bytebuffer_flush(struct bytebuffer *b, int fd) {

write(fd, b->buf, b->len);
bytebuffer_clear(b);

}

static void bytebuffer_truncate(struct bytebuffer *b, int n) {

if (n <= 0)
        return;
if (n > b->len)
        n = b->len;
const int nmove = b->len - n;
memmove(b->buf, b->buf+n, nmove);
b->len -= n;

}