aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/io/buffered_reader.v
blob: f336e2dfed890439672cb7b5ecf9deae51bbbe08 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
module io

// BufferedReader provides a buffered interface for a reader
struct BufferedReader {
mut:
	reader Reader
	buf    []byte
	offset int // current offset in the buffer
	len    int
	fails  int // how many times fill_buffer has read 0 bytes in a row
	mfails int // maximum fails, after which we can assume that the stream has ended
pub mut:
	end_of_stream bool // whether we reached the end of the upstream reader
}

// BufferedReaderConfig are options that can be given to a reader
pub struct BufferedReaderConfig {
	reader  Reader
	cap     int = 128 * 1024 // large for fast reading of big(ish) files
	retries int = 2 // how many times to retry before assuming the stream ended
}

// new_buffered_reader creates new BufferedReader
pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader {
	if o.cap <= 0 {
		panic('new_buffered_reader should be called with a positive `cap`')
	}
	// create
	r := &BufferedReader{
		reader: o.reader
		buf: []byte{len: o.cap, cap: o.cap}
		offset: 0
		mfails: o.retries
	}
	return r
}

// read fufills the Reader interface
pub fn (mut r BufferedReader) read(mut buf []byte) ?int {
	if r.end_of_stream {
		return none
	}
	// read data out of the buffer if we dont have any
	if r.needs_fill() {
		if !r.fill_buffer() {
			// end of stream
			return none
		}
	}
	read := copy(buf, r.buf[r.offset..r.len])
	if read == 0 {
		return none
	}
	r.offset += read
	return read
}

pub fn (mut r BufferedReader) free() {
	unsafe {
		r.buf.free()
	}
}

// fill_buffer attempts to refill the internal buffer
// and returns whether it got any data
fn (mut r BufferedReader) fill_buffer() bool {
	if r.end_of_stream {
		// we know we have already reached the end of stream
		// so return early
		return true
	}
	r.offset = 0
	r.len = 0
	r.len = r.reader.read(mut r.buf) or {
		// end of stream was reached
		r.end_of_stream = true
		return false
	}
	if r.len == 0 {
		r.fails++
	} else {
		r.fails = 0
	}
	if r.fails >= r.mfails {
		// When reading 0 bytes several times in a row, assume the stream has ended.
		// This prevents infinite loops ¯\_(ツ)_/¯ ...
		r.end_of_stream = true
		return false
	}
	// we got some data
	return true
}

// needs_fill returns whether the buffer needs refilling
fn (r BufferedReader) needs_fill() bool {
	return r.offset >= r.len
}

// end_of_stream returns whether the end of the stream was reached
pub fn (r BufferedReader) end_of_stream() bool {
	return r.end_of_stream
}

// read_line attempts to read a line from the buffered reader
// it will read until it finds a new line character (\n) or
// the end of stream
pub fn (mut r BufferedReader) read_line() ?string {
	if r.end_of_stream {
		return none
	}
	mut line := []byte{}
	for {
		if r.needs_fill() {
			// go fetch some new data
			if !r.fill_buffer() {
				// We are at the end of the stream
				if line.len == 0 {
					// we had nothing so return nothing
					return none
				}
				return line.bytestr()
			}
		}
		// try and find a newline character
		mut i := r.offset
		for ; i < r.len; i++ {
			c := r.buf[i]
			if c == `\n` {
				// great, we hit something
				// do some checking for whether we hit \r\n or just \n
				if i != 0 && r.buf[i - 1] == `\r` {
					x := i - 1
					line << r.buf[r.offset..x]
				} else {
					line << r.buf[r.offset..i]
				}
				r.offset = i + 1
				return line.bytestr()
			}
		}
		line << r.buf[r.offset..i]
		r.offset = i
	}
	return none
}