121 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			2.7 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
 | 
						|
	// Whether we reached the end of the upstream reader
 | 
						|
	end_of_stream bool
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
}
 | 
						|
 | 
						|
// new_buffered_reader creates new BufferedReader
 | 
						|
pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader {
 | 
						|
	assert o.cap >= 2
 | 
						|
	// create
 | 
						|
	r := &BufferedReader{
 | 
						|
		reader: o.reader
 | 
						|
		buf: []byte{len: o.cap, cap: o.cap}
 | 
						|
		offset: 0
 | 
						|
	}
 | 
						|
	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])
 | 
						|
	r.offset += read
 | 
						|
	return read
 | 
						|
}
 | 
						|
 | 
						|
// 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 false
 | 
						|
	}
 | 
						|
	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
 | 
						|
	}
 | 
						|
	// 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
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
	}
 | 
						|
}
 | 
						|
 
 |