Login
7 branches 0 tags
Ben (X13/Arch) Improved TermApp splitting ecd70d0 2 months ago 1250 Commits
nujel / lib / buffer.c
/* Nujel - Copyright (C) 2020-2022 - Benjamin Vincent Schulenburg
 * This project uses the MIT license, a copy should be included under /LICENSE */
#ifndef NUJEL_AMALGAMATION
#include "nujel-private.h"
#endif

const void *lBufferData (lBuffer *v){
	return v == NULL ? NULL : v->buf;
}

void *lBufferDataMutable(lBuffer *v){
	if(v->flags & BUFFER_IMMUTABLE){return NULL;}
	return v == NULL ? NULL : v->buf;
}

size_t lBufferLength(const lBuffer *v){
	return v == NULL ? 0 : v->length;
}

const void *lBufferViewData(lBufferView *v){
	if(unlikely(v == NULL)){return NULL;}
	if(unlikely(v->buf->flags & BUFFER_IMMUTABLE)){return NULL;}
	return &((u8 *)v->buf->buf)[v->offset * lBufferViewTypeSize(v->type)];
}

void *lBufferViewDataMutable(lBufferView *v){
	return &((u8 *)v->buf->buf)[v->offset * lBufferViewTypeSize(v->type)];
}

size_t lBufferViewLength(const lBufferView *v){
	return v->length * lBufferViewTypeSize(v->type);
}

static lVal lnfBufferAllocate(lVal a) {
	reqNaturalInt(a);
	lBuffer *buf = lBufferAlloc(a.vInt, false);
	return lValAlloc(ltBuffer, buf);
}

static lVal bufferFromPointer(bool immutable, const void *data, size_t length){
	lBuffer *retBuf = lBufferAllocRaw();
	retBuf->length = length;
	retBuf->flags = immutable ? BUFFER_IMMUTABLE : 0;
	retBuf->buf = malloc(length);
	if (unlikely(retBuf->buf == NULL)) {
		return lValException(lSymOOM, "(buffer/length!) couldn't allocate a buffer", NIL);
	}
	memcpy(retBuf->buf, data, length);

	return lValAlloc(ltBuffer, retBuf);
}

static lVal lnfBufferCopy(lVal aDest, lVal vSrc, lVal aDestOffset, lVal aLength){
	reqMutableBuffer(aDest);
	reqNaturalInt(aDestOffset);
	const int destOffset = aDestOffset.vInt;
	const void *buf = NULL;
	int length = 0;

	switch(vSrc.type){
	case ltString:
	case ltBuffer:
		buf = vSrc.vBuffer->data;
		length = castToInt(aLength, vSrc.vBuffer->length);
		break;
	}

	if(unlikely((buf == NULL) || (length < 0))){
		return lValException(lSymTypeError, "Can't copy from that", vSrc);
	}
	if(unlikely(((length + destOffset) > aDest.vBuffer->length) || (length > vSrc.vBuffer->length))){
		return lValException(lSymOutOfBounds, "Can't fit everything in that buffer", vSrc);
	}
	memcpy(&((u8*)aDest.vBuffer->buf)[destOffset], buf, length);
	return aDest;
}

static lVal lnfBufferToString(lVal a, lVal aLength, lVal aOffset){
	reqBuffer(a);
	const i64 length = MIN(a.vBuffer->length, castToInt(aLength, a.vBuffer->length));
	i64 offset = aOffset.type == ltInt ? aOffset.vInt : 0;
	if(unlikely(offset > length)){
		return lValString("");
	}
	if(unlikely(length < 0)){
		return lValException(lSymTypeError, "Length has to be greater than 0", aLength);
	}

	return lValStringLen(a.vBuffer->buf + offset, length - offset);
}

static lVal bufferView(lVal a, lVal aImmutable, lBufferViewType T){
	reqBuffer(a);
	bool immutable = castToBool(aImmutable);
	if(unlikely(!immutable && (a.vBuffer->flags & BUFFER_IMMUTABLE))){
		if(aImmutable.type == ltBool){
			return lValException(lSymTypeError, "Can't create a mutable view for an immutable buffer", a);
		} else {
			immutable = true;
		}
	}
	const size_t length = a.vBuffer->length / lBufferViewTypeSize(T);
	lBufferView *bufView = lBufferViewAlloc(a.vBuffer, T, 0, length, immutable);
	return lValAlloc(ltBufferView, bufView);
}

static lVal  lnmBufferU8(lVal a, lVal aImmutable){ return bufferView(a, aImmutable,  lbvtU8); }
static lVal  lnmBufferS8(lVal a, lVal aImmutable){ return bufferView(a, aImmutable,  lbvtS8); }
static lVal lnmBufferU16(lVal a, lVal aImmutable){ return bufferView(a, aImmutable, lbvtU16); }
static lVal lnmBufferS16(lVal a, lVal aImmutable){ return bufferView(a, aImmutable, lbvtS16); }
static lVal lnmBufferU32(lVal a, lVal aImmutable){ return bufferView(a, aImmutable, lbvtU32); }
static lVal lnmBufferS32(lVal a, lVal aImmutable){ return bufferView(a, aImmutable, lbvtS32); }
static lVal lnmBufferF32(lVal a, lVal aImmutable){ return bufferView(a, aImmutable, lbvtF32); }
static lVal lnmBufferS64(lVal a, lVal aImmutable){ return bufferView(a, aImmutable, lbvtS64); }
static lVal lnmBufferF64(lVal a, lVal aImmutable){ return bufferView(a, aImmutable, lbvtF64); }

static lVal lnmBufferViewBuffer(lVal self){
	return lValAlloc(ltBuffer, self.vBufferView->buf);
}

static lVal lnmBufferLength(lVal self){
	return lValInt(self.vBuffer->length);
}

static lVal lnmBufferViewLength(lVal self){
	return lValInt(self.vBufferView->length);
}

static lVal lnmBufferLengthSet(lVal self, lVal newLength){
	if(unlikely(self.vBuffer->flags & BUFFER_IMMUTABLE)){
		return lValException(lSymTypeError, ":length! requires a mutable buffer", self);
	}
	lBuffer *buf = self.vBuffer;

	reqNaturalInt(newLength);
	const int length = newLength.vInt;
	if(length < buf->length){
		return lValException(lSymOutOfBounds, "Buffers can only grow, not shrink.", self);
	}
	void *nBuf = realloc(buf->buf, length);
	if (unlikely(nBuf == NULL)) {
		return lValException(lSymOOM, "(buffer/length!) couldn't allocate its buffer", self);
	}
	memset(&((u8 *)nBuf)[buf->length], 0, length - buf->length);
	buf->buf = nBuf;
	buf->length = length;
	return self;
}

static lVal lnmBufferImmutable(lVal self){
	return lValBool(self.vBuffer->flags & BUFFER_IMMUTABLE);
}

static lVal lnmBufferViewImmutable(lVal self){
	return lValBool(self.vBufferView->flags & BUFFER_VIEW_IMMUTABLE);
}

static lVal lnmBufferClone(lVal self, lVal immutable){
	return bufferFromPointer(castToBool(immutable), self.vBuffer->buf, self.vBuffer->length);
}

static lVal lnmBufferCut(lVal self, lVal start, lVal stop){
	i64 slen, len;
	const char *buf = self.vBuffer->data;
	slen = len = lBufferLength(self.vBuffer);
	reqInt(start);
	i64 off = MAX(0, start.vInt);
	len = MIN(slen - off, (((stop.type == ltInt)) ? stop.vInt : len) - off);

	if(unlikely(len <= 0)){
		return lnfBufferAllocate(lValInt(0));
	}
	lBuffer *ret = lBufferAlloc(len, false);
	memcpy(lBufferDataMutable(ret), buf+off, len);
	return lValAlloc(ltBuffer, ret);
}

void lOperationsBuffer(){
	lClass *Buffer = &lClassList[ltBuffer];
	lAddNativeMethodV (Buffer, lSymS("length"),     "(self)", lnmBufferLength, NFUNC_PURE);
	lAddNativeMethodV (Buffer, lSymS("immutable?"), "(self)", lnmBufferImmutable, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("length!"),    "(self new-length)", lnmBufferLengthSet, 0);
	lAddNativeMethodVV(Buffer, lSymS("clone"),      "(self immutable?)", lnmBufferClone, NFUNC_PURE);
	lAddNativeMethodVVV(Buffer, lSymS("cut"),       "(self start stop)", lnmBufferCut, NFUNC_PURE);

	lAddNativeMethodVV(Buffer, lSymS("u8"),  "(buf immutable?)", lnmBufferU8, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("s8"),  "(buf immutable?)", lnmBufferS8, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("u16"), "(buf immutable?)", lnmBufferU16, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("s16"), "(buf immutable?)", lnmBufferS16, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("u32"), "(buf immutable?)", lnmBufferU32, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("s32"), "(buf immutable?)", lnmBufferS32, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("f32"), "(buf immutable?)", lnmBufferF32, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("s64"), "(buf immutable?)", lnmBufferS64, NFUNC_PURE);
	lAddNativeMethodVV(Buffer, lSymS("f64"), "(buf immutable?)", lnmBufferF64, NFUNC_PURE);

	lClass *BufferView = &lClassList[ltBufferView];
	lAddNativeMethodV (BufferView, lSymS("length"),     "(self)", lnmBufferViewLength, NFUNC_PURE);
	lAddNativeMethodV (BufferView, lSymS("buffer"),     "(self)", lnmBufferViewBuffer, NFUNC_PURE);
	lAddNativeMethodV (BufferView, lSymS("immutable?"), "(self)", lnmBufferViewImmutable, NFUNC_PURE);

	lAddNativeFuncV   ("buffer/allocate", "(length)",         "Allocate a new buffer of LENGTH",   lnfBufferAllocate, 0);
	lAddNativeFuncVVVV("buffer/copy",     "(dest src dest-offset length)","Return a copy of BUF that might be IMMUTABLE", lnfBufferCopy, 0);
	lAddNativeFuncVVV ("buffer->string",  "(buf length offset)",     "Turn BUF into a string of LENGTH which defaults to the size of BUF", lnfBufferToString, 0);
}