diff src/libmlk-core/core/buf.c @ 347:0969931b98e4

core: start removing POSIX APIs
author David Demelier <markand@malikania.fr>
date Tue, 19 Oct 2021 13:23:30 +0200
parents
children 460c78706989
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-core/core/buf.c	Tue Oct 19 13:23:30 2021 +0200
@@ -0,0 +1,346 @@
+/*
+ * buf.c -- simple string buffer for C
+ *
+ * Copyright (c) 2019-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "buf.h"
+
+/*
+ * Try to increase the buffer length by a power of two until we have enough
+ * space to fit `desired'.
+ *
+ * Detects overflow and return -1 if happened or reallocation could not
+ * occur.
+ */
+static int
+growdbl(struct buf *b, size_t desired)
+{
+	size_t newcap = b->capacity;
+	void *newptr;
+
+	while (desired > newcap - b->length) {
+		const size_t r = newcap * 2;
+
+		/* Overflow occured. */
+		if (r / newcap != 2) {
+#if defined(ENOMEM)
+			errno = ENOMEM;
+#endif
+			return -1;
+		}
+
+		newcap = r;
+	}
+
+	/* At this step we must have enough room. */
+	assert(newcap - b->length >= desired);
+
+	/* Pretty much impossible to reach but always assume it's possible. */
+	if (newcap == SIZE_MAX) {
+#if defined(ENOMEM)
+		errno = ENOMEM;
+#endif
+		return -1;
+	}
+
+	if (!(newptr = BUF_REALLOC(b->data, newcap + 1)))
+		return -1;
+
+	b->data = newptr;
+	b->capacity = newcap;
+
+	return 0;
+}
+
+/*
+ * Try to allocate just enough space for the `desired' amount of space. This is
+ * only used when the buffer is already too large but hasn't reached SIZE_MAX
+ * yet.
+ *
+ * Returns -1 if allocation failed.
+ */
+static int
+growmin(struct buf *b, size_t desired)
+{
+	size_t newcap;
+	void *newptr;
+
+	if (desired >= SIZE_MAX - b->length) {
+#if defined(ENOMEM)
+		errno = ENOMEM;
+#endif
+		return -1;
+	}
+
+	/* Don't forget to keep what's remaining between capacity and length. */
+	newcap = b->capacity + (desired - (b->capacity - b->length));
+
+	/* Try to reallocate. */
+	if (!(newptr = BUF_REALLOC(b->data, newcap + 1)))
+		return -1;
+
+	b->data = newptr;
+	b->capacity = newcap;
+
+	return 0;
+}
+
+/*
+ * Entry point for reallocating data. Will try to allocate twice until we have
+ * enough room and then only the minimal amount.
+ */
+static int
+grow(struct buf *b, size_t desired)
+{
+	const size_t avail = b->capacity - b->length;
+
+	if (avail >= desired)
+		return 0;
+
+	if (!b->capacity) {
+		if (!(b->data = BUF_MALLOC(desired + 1)))
+			return -1;
+
+		b->capacity = desired;
+	} else if (growdbl(b, desired) < 0 && growmin(b, desired) < 0)
+		return -1;
+
+	return 0;
+}
+
+void
+buf_init(struct buf *b)
+{
+	assert(b);
+
+	memset(b, 0, sizeof (*b));
+}
+
+int
+buf_reserve(struct buf *b, size_t amount)
+{
+	assert(b);
+
+	if (grow(b, amount) < 0)
+		return -1;
+
+	return 0;
+}
+
+int
+buf_resize(struct buf *b, size_t size, char ch)
+{
+	assert(b);
+
+	/* New size is smaller than curren't length, just update it. */
+	if (size < b->length) {
+		b->data[b->length = size] = 0;
+		return 0;
+	}
+
+	/* New size is bigger, data may be reallocated. */
+	if (grow(b, size - b->length) < 0)
+		return -1;
+
+	memset(&b->data[b->length], ch, size - b->length);
+	b->length = size;
+	b->data[b->length] = 0;
+
+	return 0;
+}
+
+int
+buf_shrink(struct buf *b)
+{
+	assert(b);
+
+	void *newptr;
+
+	if (b->length == 0) {
+		free(b->data);
+		b->data = NULL;
+		b->length = b->capacity = 0;
+		return 0;
+	}
+
+	if (!(newptr = BUF_REALLOC(b->data, b->length + 1)))
+		return -1;
+
+	b->data = newptr;
+	b->capacity = b->length;
+
+	return 0;
+}
+
+void
+buf_erase(struct buf *b, size_t pos, size_t count)
+{
+	assert(b);
+	assert(pos <= b->length);
+
+	if (count > b->length - pos) {
+		/* Optimize whole erase at pos. */
+		b->data[pos] = 0;
+		b->length = pos;
+	} else {
+		memmove(&b->data[pos], &b->data[pos + count], b->length - count);
+		b->length -= count;
+	}
+}
+
+int
+buf_putc(struct buf *b, char c)
+{
+	assert(b);
+
+	if (grow(b, 1) < 0)
+		return -1;
+
+	b->data[b->length++] = c;
+	b->data[b->length] = 0;
+
+	return 0;
+}
+
+int
+buf_puts(struct buf *b, const char *s)
+{
+	assert(b);
+	assert(s);
+
+	const size_t len = strlen(s);
+
+	if (grow(b, len) < 0)
+		return -1;
+
+	memcpy(&b->data[b->length], s, len + 1);
+	b->length += len;
+
+	return 0;
+}
+
+int
+buf_printf(struct buf *b, const char *fmt, ...)
+{
+	assert(b);
+	assert(fmt);
+
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = buf_vprintf(b, fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+int
+buf_vprintf(struct buf *b, const char *fmt, va_list args)
+{
+	assert(b);
+	assert(fmt);
+
+	va_list ap;
+	int amount;
+
+	/* Determine length. */
+	va_copy(ap, args);
+	amount = vsnprintf(NULL, 0, fmt, ap);
+	va_end(ap);
+
+	if (amount < 0)
+		return -1;
+
+	/* Do actual copy. */
+	if (grow(b, amount) < 0)
+		return -1;
+
+	va_copy(ap, args);
+	amount = vsprintf(&b->data[b->length], fmt, ap);
+	va_end(ap);
+
+	if (amount < 0)
+		return -1;
+
+	b->length += amount;
+
+	return 0;
+}
+
+int
+buf_sub(struct buf *b, const struct buf *src, size_t pos, size_t count)
+{
+	assert(b);
+	assert(src);
+	assert(pos <= src->length);
+
+	if (count >= src->length)
+		count = src->length - pos;
+	if (!(b->data = BUF_MALLOC(count + 1)))
+		return -1;
+
+	strncpy(b->data, &src->data[pos], count);
+	b->length = count;
+	b->capacity = count;
+	b->data[b->length] = 0;
+
+	return 0;
+}
+
+int
+buf_dup(struct buf *b, const struct buf *src)
+{
+	assert(b);
+	assert(src);
+
+	if (!src->data)
+		return 0;
+	if (!(b->data = BUF_MALLOC(src->length + 1)))
+		return -1;
+
+	memcpy(b->data, src->data, src->length + 1);
+	b->capacity = src->length;
+	b->length = src->length;
+
+	return 0;
+}
+
+void
+buf_clear(struct buf *b)
+{
+	assert(b);
+
+	if (b->data)
+		b->data[b->length = 0] = 0;
+}
+
+void
+buf_finish(struct buf *b)
+{
+	assert(b);
+
+	BUF_FREE(b->data);
+	b->data = NULL;
+	b->capacity = b->length = 0;
+}