aboutsummaryrefslogblamecommitdiff
path: root/kernel/lib/buffer.c
blob: 4d168e0e8c3b8223c3917f982ba294dcf1285faf (plain) (tree)








































































































































































                                                                                                  
#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 :*/