aboutsummaryrefslogtreecommitdiff
path: root/core/bufio/lookahead_reader.odin
blob: 04ce2fb6bee802250cef7d7b144df45995047be3 (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
package bufio

import "core:io"

// Loadahead_Reader provides io lookahead.
// This is useful for tokenizers/parsers.
// Loadahead_Reader is similar to bufio.Reader, but unlike bufio.Reader, Loadahead_Reader's buffer size
// will EXACTLY match the specified size, whereas bufio.Reader's buffer size may differ from the specified size.
// This makes sure that the buffer will not be accidentally read beyond the expected size.
Loadahead_Reader :: struct {
	r:   io.Reader,
	buf: []byte,
	n:   int,
}

lookahead_reader_init :: proc(lr: ^Loadahead_Reader, r: io.Reader, buf: []byte) -> ^Loadahead_Reader {
	lr.r = r;
	lr.buf = buf;
	lr.n = 0;
	return lr;
}

lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
	return lr.buf[:lr.n];
}


// lookahead_reader_peek returns a slice of the Lookahead_Reader which holds n bytes
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
// NOTE: The returned buffer is not a copy of the underlying buffer
lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Error) {
	switch {
	case n < 0:
		return nil, .Negative_Read;
	case n > len(lr.buf):
		return nil, .Buffer_Full;
	}

	n := n;
	err: io.Error;
	read_count: int;

	if lr.n < n {
		read_count, err = io.read_at_least(lr.r, lr.buf[lr.n:], n-lr.n);
		if err == .Unexpected_EOF {
			err = .EOF;
		}
	}

	lr.n += read_count;

	if n > lr.n {
		n = lr.n;
	}
	return lr.buf[:n], err;
}

// lookahead_reader_peek_all returns a slice of the Lookahead_Reader populating the full buffer
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
// NOTE: The returned buffer is not a copy of the underlying buffer
lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) {
	return lookahead_reader_peek(lr, len(lr.buf));
}


// lookahead_reader_consume drops the first n populated bytes from the Lookahead_Reader.
lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error {
	switch {
	case n == 0:
		return nil;
	case n < 0:
		return .Negative_Read;
	case lr.n < n:
		return .Short_Buffer;
	}
	copy(lr.buf, lr.buf[n:lr.n]);
	lr.n -= n;
	return nil;
}

lookahead_reader_consume_all :: proc(lr: ^Loadahead_Reader) -> io.Error {
	return lookahead_reader_consume(lr, lr.n);
}