aboutsummaryrefslogtreecommitdiff
path: root/kernel/lib/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/lib/buffer.c')
-rw-r--r--kernel/lib/buffer.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/kernel/lib/buffer.c b/kernel/lib/buffer.c
new file mode 100644
index 0000000..4d168e0
--- /dev/null
+++ b/kernel/lib/buffer.c
@@ -0,0 +1,169 @@
+#include <kmalloc.h>
+
+#include <buffer.h>
+#include <string.h>
+
+#include <sys.h>
+
+// three types of buffers
+#define T_BYTES 1
+#define T_SLICE 2
+#define T_CONCAT 3
+
+struct buffer {
+ uint16_t rc, type; // takes less space like this
+ size_t len;
+ union {
+ struct {
+ const char* data;
+ bool owned;
+ } bytes;
+ struct {
+ struct buffer *buf;
+ size_t begin;
+ } slice;
+ struct {
+ struct buffer *a, *b;
+ } concat;
+ };
+};
+
+void buffer_ref(buffer_t *b) {
+ b->rc++;
+}
+
+void buffer_unref(buffer_t *b) {
+ b->rc--;
+ if (b->rc == 0) {
+ switch (b->type) {
+ case T_BYTES:
+ if (b->bytes.owned) kfree((void*)b->bytes.data);
+ break;
+ case T_SLICE:
+ buffer_unref(b->slice.buf);
+ break;
+ case T_CONCAT:
+ buffer_unref(b->concat.a);
+ buffer_unref(b->concat.b);
+ break;
+ default:
+ ASSERT(false);
+ }
+ kfree(b);
+ }
+}
+
+size_t buffer_size(buffer_t *b) {
+ return b->len;
+}
+
+size_t read_buffer(buffer_t *b, char* dest, size_t begin, size_t n) {
+ if (begin >= b->len) return 0;
+ if (begin + n >= b->len) n = b->len - begin;
+
+ switch (b->type) {
+ case T_BYTES:
+ memcpy(dest, b->bytes.data + begin, n);
+ break;
+ case T_SLICE:
+ read_buffer(b->slice.buf, dest, begin + b->slice.begin, n);
+ break;
+ case T_CONCAT: {
+ size_t la = b->concat.a->len;
+ if (begin < la) {
+ size_t r = read_buffer(b->concat.a, dest, begin, n);
+ if (r < n) {
+ ASSERT(read_buffer(b->concat.b, dest, 0, n - r) == n - r);
+ }
+ } else {
+ ASSERT(read_buffer(b->concat.b, dest, begin - la, n) == n);
+ }
+ break;
+ }
+ default:
+ ASSERT(false);
+ }
+
+ return n;
+}
+
+// ========================= //
+// BUFFER CREATION FUNCTIONS //
+// ========================= //
+
+buffer_t *buffer_from_bytes_nocopy(const char* data, size_t n) {
+ buffer_t *b = (buffer_t*)kmalloc(sizeof(buffer_t));
+ if (b == 0) return 0;
+
+ b->rc = 1;
+ b->type = T_BYTES;
+ b->len = n;
+ b->bytes.data = data;
+ b->bytes.owned = false;
+
+ return b;
+}
+buffer_t *buffer_from_bytes(const char* data, size_t n) {
+ char* data2 = (char*)kmalloc(n);
+ if (data2 == 0) return 0;
+
+ memcpy(data2, data, n);
+
+ buffer_t *b = buffer_from_bytes_nocopy(data2, n);
+ if (b == 0) {
+ kfree(data2);
+ return 0;
+ }
+
+ b->bytes.owned = true;
+
+ return b;
+}
+
+
+buffer_t* buffer_slice(buffer_t* src, size_t begin, size_t n) {
+ if (begin + n > src->len) return 0; // invalid request
+
+ buffer_t *b = (buffer_t*)kmalloc(sizeof(buffer_t));
+ if (b == 0) return 0;
+
+ b->rc = 1;
+ b->type = T_SLICE;
+ b->len = n;
+ b->slice.buf = src;
+ b->slice.begin = begin;
+
+ return b;
+}
+
+buffer_t* buffer_concat(buffer_t* a, buffer_t* b) {
+ buffer_t *r = (buffer_t*)kmalloc(sizeof(buffer_t));
+ if (r == 0) return r;
+
+ r->rc = 1;
+ r->type = T_CONCAT;
+ r->len = a->len + b->len;
+ r->concat.a = a;
+ r->concat.b = b;
+
+ return r;
+}
+
+buffer_t* buffer_slice_k(buffer_t *b, size_t begin, size_t n) {
+ buffer_t *r = buffer_slice(b, begin, n);
+ if (r != 0) {
+ buffer_ref(b);
+ }
+ return r;
+}
+
+buffer_t* buffer_concat_k(buffer_t *a, buffer_t *b) {
+ buffer_t *r = buffer_concat(a, b);
+ if (r != 0) {
+ buffer_ref(a);
+ buffer_ref(b);
+ }
+ return r;
+}
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/