aboutsummaryrefslogtreecommitdiff
path: root/packet_reader/packet_reader.ha
blob: 30911dfa6dbaf0bfcc036a5e3c569be087637e73 (plain)
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
119
120
121
122
123
124
125
126
127
128
use io;
use fmt;
use endian;
use drawing;

export type error = !str;

export type packet_reader = struct {
	buf: []u8,
	good: []u8,
};

export fn new() packet_reader = {
	let pr = packet_reader {
		buf = alloc([0...],512*512*4*2), // ehhh
		...
	};
	pr.good = pr.buf[0..0];
	return pr;
};

fn cast_u32s_to_u8s(in: []u32) []u8 =
	(in: *[*]u32: *[*]u8)[..len(in)*4];
fn cast_u8s_to_u32s(in: []u8) []u32 =
	(in: *[*]u8: *[*]u32)[..len(in)/4];

export type packet_type = enum u8 {
	DRAW_OP,
	SEND_CHUNK,
};

export type packet_drawop = drawing::op;
export type packet_sendchunk = struct {
	world_pos: drawing::pos,
	chunk_data: []u32,
};

export type packet = (packet_drawop | packet_sendchunk);

// call when input is ready. could block otherwise
export fn read(pr: *packet_reader, sock: io::handle) (void | io::error | io::EOF) = {
	const remaining_amt = len(pr.good);
	const read_pos = if (remaining_amt > 0) {
		// still some unconsumed content in the buffer
		// move unconsumed stuff to start of buffer
		fmt::printfln("moving {} remaining bytes",remaining_amt)!;
		pr.buf[0..remaining_amt] = pr.good;
		yield remaining_amt;
	} else 0z;
	const nread = match(io::read(sock, pr.buf[read_pos..])?) {
		case io::EOF => return io::EOF;
		case let n: size => yield n;
	};
	fmt::printfln("read {} bytes",nread)!;
	const total_amt = read_pos + nread;
	pr.good = pr.buf[0..total_amt];
	fmt::printfln("now {} bytes in buffer",total_amt)!;
};

// packet format:
// u32 size
// u32 type
// [size-8]u8 data...
// size includes size of header (size and type fields)

export fn next(pr: *packet_reader) (packet | done | error) = {
	// either parse a full packet out of the front of good,
	// move good along that many bytes,
	// and return the packet,
	// or, ascertain there is no full packet, and return done
	if (len(pr.good) < size(u32)) return done;
	fmt::println("a")!;
	const packet_len = endian::legetu32(pr.good[0..4]);
	if (packet_len < 8) return "packet size field too small": error;
	fmt::println("b")!;
	if (len(pr.good) < packet_len) return done;
	fmt::println("c")!;

	const packet_bytes = pr.good[..packet_len];
	pr.good = pr.good[packet_len..];

	const ty = endian::legetu32(packet_bytes[4..8]): packet_type;
	switch (ty) {
	case packet_type::DRAW_OP =>
		const op = drawing::deser_op(packet_bytes[8..]);
		fmt::println("d")!;
		return op: packet_drawop;
	case packet_type::SEND_CHUNK =>
		// return value is BORROWED from the BUFFER
		const pos_bytes = packet_bytes[8..16];
		const chunk_data_bytes = packet_bytes[16..];
		const pos = (
			endian::legetu32(pos_bytes[0..4]): i32,
			endian::legetu32(pos_bytes[4..8]): i32
		): drawing::pos;
		const d = cast_u8s_to_u32s(chunk_data_bytes);
		assert(len(d) == 512*512,"wrong chunk size??");
		fmt::println("e")!;
		return packet_sendchunk { world_pos = pos, chunk_data = d };
	};
};

export fn send_raw(sock: io::file, ty: packet_type, datas: []u8...) (void | io::error) = {
	// ehh
	const header: [8]u8 = [0...];
	let total_len = 8u32;
	for (const data .. datas) total_len += len(data): u32;
	endian::leputu32(header[0..4], total_len);
	endian::leputu32(header[4..8], ty);
	io::writeall(sock, header)?;
	for (const data .. datas)
		io::writeall(sock, data)?;
};

export fn send(sock: io::file, packet: packet) (void | io::error) = {
	match (packet) {
	case let op: packet_drawop =>
		const ser_op = drawing::ser_op(op);
		defer free(ser_op);
		send_raw(sock, packet_type::DRAW_OP, ser_op)?;
	case let packet: packet_sendchunk =>
		const pos_buf: [8]u8 = [0...];
		endian::leputu32(pos_buf[0..4],packet.world_pos.0: u32);
		endian::leputu32(pos_buf[4..8],packet.world_pos.1: u32);
		const chunk_data_bytes = cast_u32s_to_u8s(packet.chunk_data);
		send_raw(sock, packet_type::SEND_CHUNK, pos_buf, chunk_data_bytes)?;
	};
};