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

#include "image.h"

typedef struct {
	u8 *start;
	i32 curOff;
	i32 size;
	lMap *map;
} writeImageContext;

static void writeI8(writeImageContext *ctx, i32 curOff, i32 v){
	ctx->start[curOff  ] = (v    )&0xFF;
}

static void writeI16(writeImageContext *ctx, i32 curOff, i32 v){
	ctx->start[curOff  ] = (v    )&0xFF;
	ctx->start[curOff+1] = (v>> 8)&0xFF;
}

static void writeI24(writeImageContext *ctx, i32 curOff, i32 v){
	ctx->start[curOff  ] = (v    )&0xFF;
	ctx->start[curOff+1] = (v>> 8)&0xFF;
	ctx->start[curOff+2] = (v>>16)&0xFF;
}

static void writeI32(writeImageContext *ctx, i32 curOff, i32 v){
	ctx->start[curOff  ] = (v    )&0xFF;
	ctx->start[curOff+1] = (v>> 8)&0xFF;
	ctx->start[curOff+2] = (v>>16)&0xFF;
	ctx->start[curOff+3] = (v>>24)&0xFF;
}

static i32 ctxAddVal(writeImageContext *ctx, lVal v);

static i32 writeMapGet(lMap *map, void *key){
	lVal v = lMapRef(map, lValInt((intptr_t)key));
	if(v.type == ltInt){
		return v.vInt;
	} else {
		return 0;
	}
}

static void writeMapSet(lMap *map, void *key, i32 val){
	lMapSet(map, lValInt((intptr_t)key), lValInt(val));
}

static void ctxRealloc(writeImageContext *ctx, i32 eleSize){
	if((ctx->size - ctx->curOff) <= eleSize){
		ctx->size += eleSize;
		if(ctx->size & 0xFF){
			ctx->size += 0x100 - (ctx->size & 0xFF);
		}
		ctx->start = realloc(ctx->start, ctx->size);
	}
}

static i32 ctxAddSymbol(writeImageContext *ctx, const lSymbol *v){
	if(v == NULL){return -1;}
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const i32 strLen = strnlen(v->c, sizeof(v->c));
	const i32 eleSize = strLen+1;
	ctxRealloc(ctx, eleSize);


	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	ctx->curOff += eleSize;
	memcpy(&ctx->start[curOff], v->c, strLen);
        ctx->start[curOff + strLen] = 0;
	return curOff;
}

static i32 ctxAddMap(writeImageContext *ctx, lMap *v){
	if(v == NULL){return -1;}
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const int len = v->length;
	const i32 eleSize = 2 + (6*len);
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	writeI16(ctx, curOff, len);
	ctx->curOff += eleSize;
	i32 off = curOff + 2;
	for(uint i=0;i<v->size;i++){
		if(v->entries[i].key.type == ltNil){continue;}
		writeI24(ctx, off  , ctxAddVal(ctx, v->entries[i].key));
		writeI24(ctx, off+3, ctxAddVal(ctx, v->entries[i].val));
		off += 6;
	}
	return curOff;
}
static i32 ctxAddTreeVal(writeImageContext *ctx, i32 curOff, lTree *v){
	if((v == NULL) || (v->key == NULL)){return curOff;}

	writeI24(ctx, curOff  , ctxAddSymbol(ctx, v->key));
	writeI24(ctx, curOff+3, ctxAddVal(ctx, v->value));

	curOff += 6;
	curOff = ctxAddTreeVal(ctx, curOff, v->left);
	curOff = ctxAddTreeVal(ctx, curOff, v->right);
	return curOff;
}

static i32 ctxAddTree(writeImageContext *ctx, lTree *v){
	if(v == NULL){return -1;}
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const int len = lTreeSize(v);
	const i32 eleSize = 2 + (6*len);
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	writeI16(ctx, curOff, len);
	ctx->curOff += eleSize;
	ctxAddTreeVal(ctx, curOff + 2, v);
	return curOff;
}

static i32 ctxAddArray(writeImageContext *ctx, lArray *v){
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const i32 eleSize = 4 + (4 * v->length);
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	ctx->curOff += eleSize;

	i32 *out = (i32 *)((void *)&ctx->start[curOff]);
	out[0] = (v->length & 0x0FFFFFFF) | (v->flags << 28);
	for(int i=0;i<v->length;i++){
		const i32 off = ctxAddVal(ctx, v->data[i]);
		out = (i32 *)((void *)&ctx->start[curOff + 4 + i*4]);
		*out = off;
	}

	return curOff;
}

static i32 ctxAddBytecodeArray(writeImageContext *ctx, lBytecodeArray *v){
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }
	if(v == NULL){return -1;}

	int len = v->dataEnd - v->data;
	i32 eleSize = 8 + len;
	ctxRealloc(ctx, eleSize);

	i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);

	ctx->curOff += eleSize;

	*((i32 *)((void *)&ctx->start[curOff])) = len;
	const i32 arr = ctxAddArray(ctx, v->literals);
	*((i32 *)((void *)&ctx->start[curOff+4])) = arr;
	memcpy(&ctx->start[curOff+8], v->data, len);

	return curOff;
}

static i32 ctxAddClosure(writeImageContext *ctx, lClosure *v){
	if(v == NULL){return -1;}
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const i32 eleSize = sizeof(lImageClosure);
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	ctx->curOff += eleSize;

	const i32 args = ctxAddVal(ctx, v->args);
	const i32 data = ctxAddTree(ctx, v->data);
	const i32 meta = ctxAddTree(ctx, v->meta);
	const i32 text = ctxAddBytecodeArray(ctx, v->text);
	const i32 parent = ctxAddClosure(ctx, v->parent);

	lImageClosure *out = (lImageClosure *)((void *)&ctx->start[curOff]);

	out->args = args;
	out->data = data;
	out->meta = meta;
	out->text = text;
	out->parent = parent;
	out->ip   = v->text == NULL ? 0 : (i32)((u8 *)v->ip - (u8 *)v->text->data);
	out->sp   = v->sp;
	out->type = v->type;

	return curOff;
}

static i32 ctxAddBuffer(writeImageContext *ctx, lBuffer *v){
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const i32 eleSize = 4 + v->length;
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	u32 *out = (u32 *)((void *)&ctx->start[ctx->curOff]);
	const u32 outVal = (v->length & 0x0FFFFFFF) | (v->flags << 28);
	*out = outVal;
	ctx->curOff += eleSize;

	memcpy(&out[1], v->data, v->length);
	return curOff;
}

static i32 ctxAddBufferView(writeImageContext *ctx, lBufferView *v){
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const i32 eleSize = 13;
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	ctx->curOff += eleSize;
	const i32 buf = ctxAddBuffer(ctx, v->buf);

	writeI8 (ctx, curOff+0, v->flags);
	writeI24(ctx, curOff+1, buf);
	writeI32(ctx, curOff+4, v->length);
	writeI32(ctx, curOff+8, v->offset);
	writeI8 (ctx, curOff+12, v->type);

	return curOff;
}

static i32 ctxAddPair(writeImageContext *ctx, lPair *v){
	const i32 mapOff = writeMapGet(ctx->map, (void *)v);
	if(mapOff > 0){ return mapOff; }

	const i32 eleSize = 8;
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(ctx->map, (void *)v, curOff);
	ctx->curOff += eleSize;

	const i32 car = v->car.type != ltNil ? ctxAddVal(ctx, v->car) : -1;
	writeI24(ctx, curOff, car);

	const i32 cdr = v->cdr.type != ltNil ? ctxAddVal(ctx, v->cdr) : -1;
	writeI24(ctx, curOff+3, cdr);

	return curOff;
}

static u8 ctxAddFilehandle(FILE *fh){
	if(fh == stdin){
		return 0;
	} else if(fh == stdout){
		return 1;
	} else if(fh == stderr){
		return 2;
	} else {
		return 0xFF;
	}
}

static i32 ctxAddVal(writeImageContext *ctx, lVal v){
	ctxRealloc(ctx, 16);

	const i32 curOff = ctx->curOff;
	u8 *outb = (u8 *)((void *)&ctx->start[curOff]);

	switch(v.type){
	case ltAny:
	case ltComment:
	case ltException:
		exit(234);
	case ltFileHandle:
		ctx->curOff += 2;
		*outb++ = litFileHandle;
		*outb = ctxAddFilehandle(v.vFileHandle);
		break;
	case ltNativeFunc:
		ctx->curOff += 4;
		*outb = litNativeFunc;
		writeI24(ctx, curOff+1, ctxAddSymbol(ctx, v.vNFunc->name));
		break;
	case ltType:
		ctx->curOff += 4;
		*outb = litType;
		writeI24(ctx, curOff+1, ctxAddSymbol(ctx, v.vType->name));
		break;
	case ltBytecodeArr:
		ctx->curOff += 4;
		*outb = litBytecodeArr;
		writeI24(ctx, curOff+1, ctxAddBytecodeArray(ctx, v.vBytecodeArr));
		break;
	case ltTree:
		ctx->curOff += 4;
		*outb = litTree;
		writeI24(ctx, curOff+1, ctxAddTree(ctx, v.vTree->root));
		break;
	case ltMap:
		ctx->curOff += 4;
		*outb = litMap;
		writeI24(ctx, curOff+1, ctxAddMap(ctx, v.vMap));
		break;
	case ltArray:
		ctx->curOff += 5;
		*outb = litArray;
		writeI24(ctx, curOff+1, ctxAddArray(ctx, v.vArray));
		break;
	case ltPair:
		ctx->curOff += 4;
		*outb = litPair;
		writeI24(ctx, curOff+1, ctxAddPair(ctx, v.vList));
		break;
	case ltSymbol:
	case ltKeyword:
		ctx->curOff += 4;
		*outb = v.type == ltSymbol ? litSymbol : litKeyword;
		writeI24(ctx, curOff+1, ctxAddSymbol(ctx, v.vSymbol));
		break;
	case ltBufferView:
		ctx->curOff += 4;
		*outb = litBufferView;
		writeI24(ctx, curOff+1, ctxAddBufferView(ctx, v.vBufferView));
		break;
	case ltLambda:
		ctx->curOff += 4;
		*outb = litLambda;
		writeI24(ctx, curOff+1, ctxAddClosure(ctx, v.vClosure));
		break;
	case ltMacro:
		ctx->curOff += 4;
		*outb = litMacro;
		writeI24(ctx, curOff+1, ctxAddClosure(ctx, v.vClosure));
		break;
	case ltEnvironment:
		ctx->curOff += 4;
		*outb = litEnvironment;
		writeI24(ctx, curOff+1, ctxAddClosure(ctx, v.vClosure));
		break;
	case ltString:
	case ltBuffer:
		ctx->curOff += 4;
		*outb = v.type == ltString ? litString : litBuffer;
		writeI24(ctx, curOff+1, ctxAddBuffer(ctx, v.vBuffer));
		break;
	case ltInt:
		if(v.vInt == ((i8)v.vInt)){
			ctx->curOff += 2;
			*outb++ = litInt8;
			*outb++ = v.vInt;
		} else if(v.vInt == ((i16)v.vInt)){
			*outb++ = litInt16;
			ctx->curOff += 3;
			i16 *out = (i16 *)((void *)&ctx->start[curOff+1]);
			*out = v.vInt;
		} else if(v.vInt == ((i32)v.vInt)){
			*outb++ = litInt32;
			ctx->curOff += 5;
			i32 *out = (i32 *)((void *)&ctx->start[curOff+1]);
			*out = v.vInt;
		} else {
			*outb++ = litInt64;
			ctx->curOff += 9;
			u64 *outq = (u64 *)((void *)&ctx->start[curOff+1]);
			*outq = v.vInt;
		}
		break;
	case ltFloat: {
		ctx->curOff += 9;
		*outb = litFloat;
		double *outf = (double *)((void *)&ctx->start[curOff+1]);
		*outf = v.vFloat;
		break; }
	case ltBool:
		ctx->curOff += 1;
		*outb = v.vBool ? litTrue : litFalse;
		break;
	case ltNil:
		ctx->curOff += 1;
		*outb = litNil;
		break;
	}
	return curOff;
}

static lImage *writeImage(lVal rootValue, i32 *outSize){
	size_t size = sizeof(lImage);
	writeImageContext ctx;
	memset(&ctx, 0, sizeof(ctx));
	ctx.map = lMapAllocRaw();
	lImage *buf = malloc(size);

	buf->magic[0] = 'N';
	buf->magic[1] = 'u';
	buf->magic[2] = 'j';
	buf->magic[3] = 'I';

	ctxAddVal(&ctx, rootValue);

	buf = realloc(buf, sizeof(lImage) + ctx.curOff);
	memcpy(buf->data, ctx.start, ctx.curOff);
	size += ctx.curOff;
	*outSize = size;
	free(ctx.start);
	return buf;
}

lVal lnfSerialize(lVal val){
	i32 size;
	lImage *img = writeImage(val, &size);
	if(unlikely(img == NULL)){
		return lValException(lSymTypeError, "Can't serialize that", NIL);
	} else {
		lBuffer *buf = lBufferAlloc(size, true);
		buf->buf = img;
		return lValAlloc(ltBuffer, buf);
	}
}