Login
7 branches 0 tags
Ben (X13/Arch) Removed closure/resolve fn's c7c5a28 2 years ago 1153 Commits
nujel / lib / image.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 <string.h>

typedef struct {
	u8 magic[4]; // NujI
	u8 data[];
} lImage;

typedef enum {
	litNil = 0,

	litSymbol,
	litKeyword,

	litInt8,
	litInt16,
	litInt32,
	litInt64,

	litFloat,

	litPair,
	litArray,
	litTree,

	litLambda,
	litMacro,
	litNativeFunc,
	litEnvironment,

	litString,
	litBuffer,
	litBufferView,
	litBytecodeArr,

	litFileHandle,
	litType,

	litTrue,
	litFalse,

} lImageType;

typedef struct {
	i32 buffer;
	i32 length;
	i32 offset;
	u8 flags;
	u8 type;
} lImageBufferView;

typedef struct {
	i32 length;
	u8 flags;
	u8 data[];
} lImageBuffer;

typedef struct {
	i32 parent;
	i32 data;
	i32 meta;
	i32 text;
	i32 args;
	i32 ip;
	u16 sp;
	u8 type;
} lImageClosure;

typedef struct {
	i32 imgSize;
	i32 len;
	i32 size;
	i32 *key;
	void **val;
} readImageMap;

typedef struct {
	i32 len;
	i32 size;
	void **key;
	i32 *val;
} writeImageMap;

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

static lVal readVal(readImageMap *map, const lImage *img, i32 off, bool staticImage);
static i32 ctxAddVal(writeImageContext *ctx, lVal v);

static i32 writeMapGet(writeImageMap *map, void *key){
	for(int i=0;i<map->len;i++){
		if(map->key[i] == key){
			return map->val[i];
		}
	}
	return 0;
}

static void writeMapSet(writeImageMap *map, void *key, i32 val){
	if(map->len+1 >= map->size){
		map->size += 32;
		map->key = realloc(map->key, map->size * sizeof(void *));
		map->val = realloc(map->val, map->size * sizeof(i32));
	}
	map->key[map->len] = key;
	map->val[map->len] = val;
	map->len++;
}

static void *readMapGet(readImageMap *map, i32 key){
	for(int i=0;i<map->len;i++){
		if(map->key[i] == key){
			return map->val[i];
		}
	}
	return NULL;
}

static void readMapSet(readImageMap *map, i32 key, void *val){
	if(map->len+1 >= map->size){
		map->size += 32;
		map->key = realloc(map->key, map->size * sizeof(i32));
		map->val = realloc(map->val, map->size * sizeof(void *));
	}
	map->key[map->len] = key;
	map->val[map->len] = val;
	map->len++;
}

static const lSymbol *readSymbol(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	(void)staticImage;
	(void)map;
	if(off < 0){return NULL;}
	return lSymS((const char *)&img->data[off]);
}

static lArray *readArray(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	if(off < 0){return NULL;}
	const void *mapP = readMapGet(map, off);
	if(mapP != NULL){ return (lArray *)mapP; }
	const i32 *in = (i32 *)((void *)&img->data[off]);
	const i32 len = in[0];
	lArray *out = lArrayAlloc(len);
	readMapSet(map, off, out);
	out->flags = in[1];
	in = &in[2];
	for(int i=0;i<len;i++){
		out->data[i] = readVal(map, img, in[i], staticImage);
	}
	return out;
}

static lBytecodeArray *readBytecodeArray(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	if(off < 0){return NULL;}
	const void *mapP = readMapGet(map, off);
	if(mapP != NULL){ return (lBytecodeArray *)mapP; }
	const i32 *in = (i32 *)((void *)&img->data[off]);
	const i32 len = *in++;
	lBytecodeArray *ret = lBytecodeArrayAllocRaw();
	readMapSet(map, off, ret);
	ret->literals = readArray(map, img, *in++, staticImage);
	if(staticImage){
		ret->data = (void *)in;
	} else {
		ret->data = malloc(len);
		memcpy(ret->data, in, len);
	}
	ret->dataEnd = &ret->data[len];
	return ret;
}

static lPair *readPair(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	const void *mapP = readMapGet(map, off);
	if(off < 0){return NULL;}
	if(mapP != NULL){ return (lPair *)mapP; }
	const i32 *pair = (const i32 *)((void *)&img->data[off]);
	lPair *ret = lPairAllocRaw();
	readMapSet(map, off, ret);
	ret->car = pair[0] >= 0 ? readVal(map, img, pair[0], staticImage) : NIL;
	ret->cdr = pair[1] >= 0 ? readVal(map, img, pair[1], staticImage) : NIL;
	return ret;
}

static lBuffer *readBuffer(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	const void *mapP = readMapGet(map, off);
	if(mapP != NULL){ return (lBuffer *)mapP; }
	if(off < 0){return NULL;}
	lImageBuffer *imgBuf = (lImageBuffer *)((void *)&img->data[off]);
	bool immutable = imgBuf->flags & BUFFER_IMMUTABLE;
	lBuffer *buf = lBufferAlloc(imgBuf->length, immutable);
	readMapSet(map, off, buf);
	if(staticImage && immutable){
		buf->buf = imgBuf->data;
		buf->flags |= BUFFER_STATIC;
	} else {
		buf->buf = malloc(imgBuf->length);
		memcpy(buf->buf, imgBuf->data, imgBuf->length);
	}
	return buf;
}

static lBufferView *readBufferView(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	const void *mapP = readMapGet(map, off);
	if(mapP != NULL){ return (lBufferView *)mapP; }
	if(off < 0){return NULL;}
	lImageBufferView *imgBuf = (lImageBufferView *)((void *)&img->data[off]);
	lBuffer *buf = readBuffer(map, img, imgBuf->buffer, staticImage);
	lBufferView *view = lBufferViewAllocRaw();
	readMapSet(map, off, view);
	view->buf = buf;
	view->offset = imgBuf->offset;
	view->length = imgBuf->length;
	view->flags = imgBuf->flags;
	view->type = imgBuf->type;
	return view;
}

static lTree *readTree(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	const void *mapP = readMapGet(map, off);
	if(off < 0){return NULL;}
	if(mapP != NULL){ return (lTree *)mapP; }
	const i32 *in = (i32 *)((void *)&img->data[off]);
	const i32 len = *in++;
	lTree *ret = lTreeAllocRaw();
	readMapSet(map, off, ret);
	for(int i=0;i<len;i++){
		const lSymbol *s = readSymbol(map, img, *in++, staticImage);
		lVal v = readVal(map, img, *in++, staticImage);
		ret = lTreeInsert(ret, s, v);
	}
	return ret;
}

static lNFunc *readNFunc(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	(void)map;
	if(off < 0){return NULL;}
	const lSymbol *sym = readSymbol(map, img, off, staticImage);
	for(uint i=0;i<lNFuncMax;i++){
		lNFunc *t = &lNFuncList[i];
		if(t == NULL){break;}
		if(t->name == sym){
			return t;
		}
	}
	return NULL;
}

static lClass *readType(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	(void)map;
	const lSymbol *sym = readSymbol(map, img, off, staticImage);
	if(off < 0){return NULL;}
	for(uint i=0;i<countof(lClassList);i++){
		lClass *t = &lClassList[i];
		if(t == NULL){break;}
		if(t->name == sym){
			return t;
		}
	}
	return NULL;
}

static lClosure *readClosure(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	const void *mapP = readMapGet(map, off);
	if(mapP != NULL){ return (lClosure *)mapP; }
	if(off < 0){return NULL;}
	lImageClosure *clo = (lImageClosure *)((void *)&img->data[off]);
	lClosure *ret = lClosureAllocRaw();
	readMapSet(map, off, ret);

	ret->args = readVal(map, img, clo->args, staticImage);
	ret->data = readTree(map, img, clo->data, staticImage);
	ret->meta = readTree(map, img, clo->meta, staticImage);
	ret->parent = readClosure(map, img, clo->parent, staticImage);
	ret->sp = clo->sp;
	ret->text = readBytecodeArray(map, img, clo->text, staticImage);
	ret->type = clo->type;
	if(ret->text != NULL){
		ret->ip = &ret->text->data[clo->ip];
	} else {
		ret->ip = NULL;
	}

	return ret;
}

static lVal readVal(readImageMap *map, const lImage *img, i32 off, bool staticImage){
	if(off < 0){return NIL;}
	const lImageType T = img->data[off++];
	const i32 *dword = (i32 *)((void *)&img->data[off]);

	switch(T){
	default:
		return lValException(lSymReadError, "Can't have rootValues of that Type", NIL);
	case litFileHandle:
		switch(img->data[off]){
		case 0:
			return lValFileHandle(stdin);
		case 1:
			return lValFileHandle(stdout);
		case 2:
			return lValFileHandle(stderr);
		default:
			return lValException(lSymReadError, "Can't serialize file handles other than stdin, stdout or stderr", NIL);
		}
	case litNativeFunc:
		return lValAlloc(ltNativeFunc, readNFunc(map, img, *dword, staticImage));
	case litType:
		return lValAlloc(ltType, readType(map, img, *dword, staticImage));
	case litBytecodeArr:
		return lValAlloc(ltBytecodeArr, readBytecodeArray(map, img, *dword, staticImage));
	case litTree:
		return lValTree(readTree(map, img, *dword, staticImage));
	case litArray:
		return lValAlloc(ltArray, readArray(map, img, *dword, staticImage));
	case litPair:
		return lValAlloc(ltPair, readPair(map, img, *dword, staticImage));
	case litBufferView:
		return lValAlloc(ltBufferView, readBufferView(map, img, *dword, staticImage));
	case litLambda:
		return lValAlloc(ltLambda, readClosure(map, img, *dword, staticImage));
	case litMacro:
		return lValAlloc(ltMacro, readClosure(map, img, *dword, staticImage));
	case litEnvironment:
		return lValAlloc(ltEnvironment, readClosure(map, img, *dword, staticImage));
	case litString:
		return lValAlloc(ltString, readBuffer(map, img, *dword, staticImage));
	case litBuffer:
		return lValAlloc(ltBuffer, readBuffer(map, img, *dword, staticImage));
	case litSymbol:
		return lValAlloc(ltSymbol, (void *)readSymbol(map, img, *dword, staticImage));
	case litKeyword:
		return lValAlloc(ltKeyword, (void *)readSymbol(map, img, *dword, staticImage));
	case litNil:
		return NIL;
	case litInt64:
		return lValInt(*(i64 *)((void *)&img->data[off]));
	case litInt32:
		return lValInt(*(i32 *)((void *)&img->data[off]));
	case litInt16:
		return lValInt(*(i16 *)((void *)&img->data[off]));
	case litInt8:
		return lValInt(*(i8 *)((void *)&img->data[off]));
	case litFloat: {
		const double *fword = (double *)((void *)&img->data[off]);
		return lValFloat(*fword);
	}
	case litFalse:
		return lValBool(false);
	case litTrue:
		return lValBool(true);
	}
	return NIL;
}

lVal readImage(const void *ptr, size_t imgSize, bool staticImage){
	const lImage *img = ptr;
	if(unlikely(img == NULL)){
		return lValExceptionSimple();
	}
	if(unlikely((img->magic[0] != 'N')
	   || (img->magic[1] != 'u')
	   || (img->magic[2] != 'j')
	   || (img->magic[3] != 'I'))){
		return lValException(lSymReadError, "Invalid Magic prefix in Nujel Image", NIL);
	}
	readImageMap map;
	memset(&map, 0, sizeof(map));
	map.imgSize = imgSize-4;

	lVal ret = readVal(&map, img, 0, staticImage);
	free(map.key);
	free(map.val);
	return ret;
}

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){
	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 ctxAddTreeVal(writeImageContext *ctx, i32 curOff, lTree *v){
	if(v == NULL){return curOff;}

	const i32 sym = ctxAddSymbol(ctx, v->key);
	const i32 val = ctxAddVal(ctx, v->value);
	i32 *out = (i32 *)((void *)&ctx->start[curOff]);
	out[0] = sym;
	out[1] = val;
	curOff += 8;
	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 = 4 + (4*2*len);
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(&ctx->map, (void *)v, curOff);
	i32 *out = (i32 *)((void *)&ctx->start[ctx->curOff]);
	*out = len;
	ctx->curOff += eleSize;
	ctxAddTreeVal(ctx, curOff + 4, 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 = 8 + (4 * v->length);
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(&ctx->map, (void *)v, curOff);
	i32 *out = (i32 *)((void *)&ctx->start[ctx->curOff]);
	ctx->curOff += eleSize;

	out[0] = v->length;
	out[1] = v->flags;
	for(int i=0;i<v->length;i++){
		const i32 off = ctxAddVal(ctx, v->data[i]);
		out = (i32 *)((void *)&ctx->start[curOff + 8 + 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->ip   = v->text == NULL ? 0 : (i32)((u8 *)v->ip - (u8 *)v->text->data);
	out->parent = parent;
	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 = sizeof(lImageBuffer) + v->length;
	ctxRealloc(ctx, eleSize);

	const i32 curOff = ctx->curOff;
	writeMapSet(&ctx->map, (void *)v, curOff);
	lImageBuffer *out = (lImageBuffer *)((void *)&ctx->start[ctx->curOff]);
	ctx->curOff += eleSize;

	out->flags = v->flags;
	out->length = v->length;
	memcpy(out->data, 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 = sizeof(lImageBufferView);
	ctxRealloc(ctx, eleSize);

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

	lImageBufferView *out = (lImageBufferView *)((void *)&ctx->start[curOff]);
	out->buffer = buf;
	out->length = v->length;
	out->offset = v->offset;
	out->flags = v->flags;
	out->type = 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;
	const i32 cdr = v->cdr.type != ltNil ? ctxAddVal(ctx, v->cdr) : -1;
	i32 *out = (i32 *)((void *)&ctx->start[curOff]);
	out[0] = car;
	out[1] = 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 += 5;
		*outb = litNativeFunc;
		const i32 off = ctxAddSymbol(ctx, v.vNFunc->name);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltType: {
		ctx->curOff += 5;
		*outb = litType;
		const i32 off = ctxAddSymbol(ctx, v.vType->name);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltBytecodeArr: {
		ctx->curOff += 5;
		*outb = litBytecodeArr;
		const i32 off = ctxAddBytecodeArray(ctx, v.vBytecodeArr);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltTree: {
		ctx->curOff += 5;
		*outb = litTree;
		const i32 off = ctxAddTree(ctx, v.vTree->root);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltArray: {
		ctx->curOff += 5;
		*outb = litArray;
		const i32 off = ctxAddArray(ctx, v.vArray);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltPair: {
		ctx->curOff += 5;
		*outb = litPair;
		const i32 off = ctxAddPair(ctx, v.vList);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltSymbol:
	case ltKeyword: {
		ctx->curOff += 5;
		*outb = v.type == ltSymbol ? litSymbol : litKeyword;
		const i32 off = ctxAddSymbol(ctx, v.vSymbol);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltBufferView: {
		ctx->curOff += 5;
		*outb = litBufferView;
		const i32 off = ctxAddBufferView(ctx, v.vBufferView);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltLambda: {
		ctx->curOff += 5;
		*outb = litLambda;
		const i32 off = ctxAddClosure(ctx, v.vClosure);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltMacro: {
		ctx->curOff += 5;
		*outb = litMacro;
		const i32 off = ctxAddClosure(ctx, v.vClosure);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltEnvironment: {
		ctx->curOff += 5;
		*outb = litEnvironment;
		const i32 off = ctxAddClosure(ctx, v.vClosure);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		break; }
	case ltString:
	case ltBuffer: {
		ctx->curOff += 5;
		*outb = v.type == ltString ? litString : litBuffer;
		const i32 off = ctxAddBuffer(ctx, v.vBuffer);
		i32 *outd = (i32 *)((void *)&ctx->start[curOff+1]);
		*outd = off;
		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));
	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);
	free(ctx.map.key);
	free(ctx.map.val);
	return buf;
}

static 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);
	}
}

static lVal lnfDeserialize(lVal val){
	reqBuffer(val);
	return readImage(val.vBuffer->buf, val.vBuffer->length, false);
}

void lOperationsImage(lClosure *c){
	lAddNativeFuncV(c,"image/serialize",   "(val)", "Serializes val into a binary representation that can be stored", lnfSerialize, 0);
	lAddNativeFuncV(c,"image/deserialize", "(buf)", "Deserializes buf into a value", lnfDeserialize, 0);
}