2020-11-15 23:54:47 +03:00
|
|
|
module io
|
|
|
|
|
|
|
|
// BufferedReader provides a buffered interface for a reader
|
|
|
|
struct BufferedReader {
|
|
|
|
mut:
|
2020-12-16 20:22:26 +03:00
|
|
|
reader Reader
|
|
|
|
buf []byte
|
2020-11-15 23:54:47 +03:00
|
|
|
// current offset in the buffer
|
2020-12-16 20:22:26 +03:00
|
|
|
offset int
|
|
|
|
len int
|
|
|
|
// Whether we reached the end of the upstream reader
|
|
|
|
end_of_stream bool
|
2020-11-15 23:54:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// BufferedReaderConfig are options that can be given to a reader
|
|
|
|
pub struct BufferedReaderConfig {
|
2020-12-16 20:22:26 +03:00
|
|
|
reader Reader
|
|
|
|
cap int = 128 * 1024 // large for fast reading of big(ish) files
|
2020-11-15 23:54:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// new_buffered_reader creates new BufferedReader
|
|
|
|
pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader {
|
2020-12-16 20:22:26 +03:00
|
|
|
assert o.cap >= 2
|
2020-11-15 23:54:47 +03:00
|
|
|
// create
|
|
|
|
r := &BufferedReader{
|
|
|
|
reader: o.reader
|
2020-12-16 20:22:26 +03:00
|
|
|
buf: []byte{len: o.cap, cap: o.cap}
|
2020-11-15 23:54:47 +03:00
|
|
|
offset: 0
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
// read fufills the Reader interface
|
|
|
|
pub fn (mut r BufferedReader) read(mut buf []byte) ?int {
|
|
|
|
// read data out of the buffer if we dont have any
|
2020-12-16 20:22:26 +03:00
|
|
|
if r.needs_fill() {
|
|
|
|
if !r.fill_buffer() {
|
|
|
|
// end of stream
|
|
|
|
return none
|
|
|
|
}
|
2020-11-15 23:54:47 +03:00
|
|
|
}
|
|
|
|
read := copy(buf, r.buf[r.offset..r.len])
|
|
|
|
r.offset += read
|
|
|
|
return read
|
|
|
|
}
|
|
|
|
|
2020-12-16 20:22:26 +03:00
|
|
|
// 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
|
2021-01-07 22:21:47 +03:00
|
|
|
return true
|
2020-12-16 20:22:26 +03:00
|
|
|
}
|
2020-11-15 23:54:47 +03:00
|
|
|
r.offset = 0
|
2020-12-16 20:22:26 +03:00
|
|
|
r.len = 0
|
|
|
|
r.len = r.reader.read(mut r.buf) or {
|
|
|
|
// end of stream was reached
|
|
|
|
r.end_of_stream = true
|
|
|
|
return false
|
2020-11-15 23:54:47 +03:00
|
|
|
}
|
2020-12-16 20:22:26 +03:00
|
|
|
// 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 - 1
|
2020-11-15 23:54:47 +03:00
|
|
|
}
|
|
|
|
|
2020-12-16 20:22:26 +03:00
|
|
|
// 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
|
2020-11-15 23:54:47 +03:00
|
|
|
pub fn (mut r BufferedReader) read_line() ?string {
|
2020-12-16 20:22:26 +03:00
|
|
|
if r.end_of_stream {
|
|
|
|
return none
|
|
|
|
}
|
2020-11-15 23:54:47 +03:00
|
|
|
mut line := []byte{}
|
|
|
|
for {
|
2020-12-16 20:22:26 +03:00
|
|
|
if r.needs_fill() {
|
2020-11-15 23:54:47 +03:00
|
|
|
// go fetch some new data
|
2020-12-16 20:22:26 +03:00
|
|
|
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()
|
|
|
|
}
|
2020-11-15 23:54:47 +03:00
|
|
|
}
|
|
|
|
// 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
|
2020-12-16 20:22:26 +03:00
|
|
|
if i != 0 && r.buf[i - 1] == `\r` {
|
|
|
|
x := i - 1
|
2020-11-15 23:54:47 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|