v/vlib/io/buffered_reader.v

101 lines
2.1 KiB
V

module io
// BufferedReader provides a buffered interface for a reader
struct BufferedReader {
mut:
reader Reader
buf []byte
// current offset in the buffer
offset int
len int
}
// BufferedReaderConfig are options that can be given to a reader
pub struct BufferedReaderConfig {
reader Reader
buf_cap int = 128 * 1024 // large for fast reading of big(ish) files
}
// new_buffered_reader creates new BufferedReader
pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader {
assert o.buf_cap >= 2
// create
r := &BufferedReader{
reader: o.reader
buf: []byte{len: o.buf_cap, cap: o.buf_cap}
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
if r.offset >= r.len-1 {
r.fill_buffer()?
}
read := copy(buf, r.buf[r.offset..r.len])
r.offset += read
return read
}
// fill buffer attempts to refill the internal buffer
fn (mut r BufferedReader) fill_buffer() ? {
// TODO we should keep track of when we get an end of stream
// from the upstream reader so that we dont have to keep
// trying to call this
r.offset = 0
new_len := r.reader.read(mut r.buf) or {
if errcode != 0 || err.len != 0 {
eprintln('>> BufferedReader.reader.read err: $err | errcode: $errcode')
}
0
}
r.len = new_len
}
// read_line reads a line from the buffered reader
pub fn (mut r BufferedReader) read_line() ?string {
mut line := []byte{}
for {
if r.offset >= (r.len-1) {
// go fetch some new data
r.fill_buffer()?
}
if r.len == 0 {
// if we have no data then return nothing
return none
}
// 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
}
}