summaryrefslogtreecommitdiffstats
path: root/bytecode/encoding.c
blob: 62c9762093c4fa88fbf23cc11170f7b46c6b917b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <stdint.h>
#include <stdlib.h>

#include "panic.h"
#include "slice.h"
#include "value.h"

#include "encoding.h"

struct reader {
	struct slice data;
	int n;
};

static void advance(struct reader *r, int size)
{
	r->data = slice1(r->data, size);
	r->n += size;
}

static uint8_t parse_byte(struct reader *r)
{
	if (r->data.size < 1) {
		panicf("read byte: no data!\n");
	}
	uint8_t res = r->data.buf[0];
	advance(r, 1);
	return res;
}

static local parse_local(struct reader *r)
{
	if (r->data.size < 1) {
		panicf("local: no data!\n");
	}
	local res = r->data.buf[0];
	if (res > 7) {
		panicf("Invalid local (out of range): %d\n", res);
	}
	advance(r, 1);
	return res;
}

static value parse_value(struct reader *r)
{
	if (r->data.size < 8) {
		panicf("value: no data!\n");
	}
	uint64_t res = *(uint64_t *) r->data.buf;
	advance(r, 8);
	return res;
}

static struct arg parse_arg(struct reader *r, bool is_const)
{
	if (is_const) {
		return (struct arg) { .constant = parse_value(r) };
	}
	return (struct arg) { .local = parse_local(r) };
}

static uint8_t parse_offset(struct reader *r)
{
	if (r->data.size < 1) {
		panicf("offset: no data!\n");
	}
	uint8_t res = r->data.buf[0];
	advance(r, 1);
	return res;
}

struct result parse(struct slice data)
{
	struct reader r = { .data = data, .n = 0 };

	uint8_t codeByte = parse_byte(&r);
	enum opcode code = codeByte >> 2;
	bool arg1_const = codeByte & 0x2;
	// bool arg2_const = codeByte & 0x1;

	struct op out = {
		.code = code,
	};

	switch (code) {
	case OALLOC:
		out.alloc.out = parse_local(&r);
		out.alloc.size = parse_arg(&r, arg1_const);
		break;
	case OCALL:
		// No arguments.
		break;
	case OPOKE:
		out.poke.offset = parse_offset(&r);
		out.poke.pointer = parse_local(&r);
		out.poke.value = parse_arg(&r, arg1_const);
		break;
	case OPEEK:
		out.peek.out = parse_local(&r);
		out.peek.offset = parse_offset(&r);
		out.peek.value = parse_arg(&r, arg1_const);
		break;
	case OSHUF:
		out.shuf.out = parse_local(&r);
		out.shuf.value = parse_arg(&r, arg1_const);
		break;
	case OEXIT:
		out.exit.value = parse_arg(&r, arg1_const);
		break;
	default:
		panicf("Invalid code %d\n", code);
	}

	return (struct result) {
		.op = out,
		.n = r.n,
	};
}