summary refs log tree commit diff
path: root/bin/edi/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/edi/buffer.c')
-rw-r--r--bin/edi/buffer.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/bin/edi/buffer.c b/bin/edi/buffer.c
new file mode 100644
index 00000000..e9422951
--- /dev/null
+++ b/bin/edi/buffer.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2018  Curtis McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <wchar.h>
+
+#include "edi.h"
+
+static struct Block *blockAlloc(struct Block *prev, size_t cap) {
+	size_t size = sizeof(struct Block) + sizeof(wchar_t) * cap;
+	struct Block *block = malloc(size);
+	if (!block) err(EX_OSERR, "malloc");
+	block->prev = prev;
+	return block;
+}
+
+struct Buffer bufferAlloc(size_t cap) {
+	struct Block *block = blockAlloc(NULL, cap);
+	return (struct Buffer) {
+		.cap = cap,
+		.len = 0,
+		.slice = { block->chars, 0 },
+		.block = block,
+	};
+}
+
+void bufferFree(struct Buffer *buf) {
+	struct Block *prev;
+	for (struct Block *it = buf->block; it; it = prev) {
+		prev = it->prev;
+		free(it);
+	}
+}
+
+void bufferInsert(struct Buffer *buf) {
+	buf->slice.ptr = &buf->block->chars[buf->len];
+	buf->slice.len = 0;
+}
+
+void bufferAppend(struct Buffer *buf, wchar_t ch) {
+	if (buf->len == buf->cap) {
+		if (buf->slice.len == buf->cap) buf->cap *= 2;
+		struct Block *block = blockAlloc(buf->block, buf->cap);
+		memcpy(block->chars, buf->slice.ptr, sizeof(wchar_t) * buf->slice.len);
+		buf->len = buf->slice.len;
+		buf->slice.ptr = block->chars;
+		buf->block = block;
+	}
+	buf->block->chars[buf->len++] = ch;
+	buf->slice.len++;
+}
+
+wchar_t *bufferDest(struct Buffer *buf, size_t len) {
+	if (buf->len + len > buf->cap) {
+		while (len > buf->cap) buf->cap *= 2;
+		buf->block = blockAlloc(buf->block, buf->cap);
+		buf->len = 0;
+	}
+	wchar_t *ptr = &buf->block->chars[buf->len];
+	buf->slice.ptr = ptr;
+	buf->slice.len = len;
+	buf->len += len;
+	return ptr;
+}
+
+#ifdef TEST
+#include <assert.h>
+
+int main() {
+	struct Buffer buf = bufferAlloc(6);
+
+	bufferInsert(&buf);
+	bufferAppend(&buf, L'A');
+	bufferAppend(&buf, L'B');
+	assert(!wcsncmp(L"AB", buf.slice.ptr, buf.slice.len));
+
+	bufferInsert(&buf);
+	bufferAppend(&buf, L'C');
+	bufferAppend(&buf, L'D');
+	assert(!wcsncmp(L"CD", buf.slice.ptr, buf.slice.len));
+
+	bufferInsert(&buf);
+	bufferAppend(&buf, L'E');
+	bufferAppend(&buf, L'F');
+	bufferAppend(&buf, L'G');
+	bufferAppend(&buf, L'H');
+	assert(!wcsncmp(L"EFGH", buf.slice.ptr, buf.slice.len));
+
+	bufferFree(&buf);
+
+	buf = bufferAlloc(4);
+	bufferInsert(&buf);
+	bufferAppend(&buf, L'A');
+	bufferAppend(&buf, L'B');
+	bufferAppend(&buf, L'C');
+	bufferAppend(&buf, L'D');
+	bufferAppend(&buf, L'E');
+	bufferAppend(&buf, L'F');
+	assert(!wcsncmp(L"ABCDEF", buf.slice.ptr, buf.slice.len));
+	bufferFree(&buf);
+
+	buf = bufferAlloc(4);
+
+	wchar_t *dest = bufferDest(&buf, 2);
+	dest[0] = L'A';
+	dest[1] = L'B';
+	assert(!wcsncmp(L"AB", buf.slice.ptr, buf.slice.len));
+
+	dest = bufferDest(&buf, 3);
+	dest[0] = L'C';
+	dest[1] = L'D';
+	dest[2] = L'E';
+	assert(!wcsncmp(L"CDE", buf.slice.ptr, buf.slice.len));
+
+	bufferFree(&buf);
+
+	buf = bufferAlloc(4);
+	dest = bufferDest(&buf, 6);
+	dest[0] = L'A';
+	dest[1] = L'B';
+	dest[2] = L'C';
+	dest[3] = L'D';
+	dest[4] = L'E';
+	dest[5] = L'F';
+	assert(!wcsncmp(L"ABCDEF", buf.slice.ptr, buf.slice.len));
+	bufferFree(&buf);
+}
+
+#endif