io: fix detection of end_of_stream, when reading files through io.new_buffered_reader(reader: io.make_reader(f))

pull/8791/head
Delyan Angelov 2021-02-16 22:53:46 +02:00
parent 843de10442
commit 4961d3ea17
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
3 changed files with 57 additions and 16 deletions

View File

@ -5,17 +5,15 @@ import io
fn main() { fn main() {
// Make a new connection // Make a new connection
mut conn := net.dial_tcp('google.com:80')? mut conn := net.dial_tcp('google.com:80') ?
// Simple http HEAD request for a file // Simple http HEAD request for a file
conn.write_str('GET /index.html HTTP/1.0\r\n\r\n')? conn.write_str('GET /index.html HTTP/1.0\r\n\r\n') ?
// Wrap in a buffered reader // Wrap in a buffered reader
mut r := io.new_buffered_reader(reader: io.make_reader(conn)) mut r := io.new_buffered_reader(reader: io.make_reader(conn))
for { for {
l := r.read_line() or { l := r.read_line() or { break }
break
}
println('$l') println('$l')
// Make it nice and obvious that we are doing this line by line // Make it nice and obvious that we are doing this line by line
time.sleep_ms(10) time.sleep_ms(100)
} }
} }

View File

@ -3,19 +3,21 @@ module io
// BufferedReader provides a buffered interface for a reader // BufferedReader provides a buffered interface for a reader
struct BufferedReader { struct BufferedReader {
mut: mut:
reader Reader reader Reader
buf []byte buf []byte
// current offset in the buffer offset int // current offset in the buffer
offset int len int
len int fails int // how many times fill_buffer has read 0 bytes in a row
// Whether we reached the end of the upstream reader mfails int // maximum fails, after which we can assume that the stream has ended
end_of_stream bool 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 // BufferedReaderConfig are options that can be given to a reader
pub struct BufferedReaderConfig { pub struct BufferedReaderConfig {
reader Reader reader Reader
cap int = 128 * 1024 // large for fast reading of big(ish) files 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 // new_buffered_reader creates new BufferedReader
@ -26,6 +28,7 @@ pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader {
reader: o.reader reader: o.reader
buf: []byte{len: o.cap, cap: o.cap} buf: []byte{len: o.cap, cap: o.cap}
offset: 0 offset: 0
mfails: o.retries
} }
return r return r
} }
@ -59,6 +62,17 @@ fn (mut r BufferedReader) fill_buffer() bool {
r.end_of_stream = true r.end_of_stream = true
return false 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 // we got some data
return true return true
} }

View File

@ -0,0 +1,29 @@
import os
import io
fn read_file(file string, cap int) []string {
mut lines := []string{}
mut f := os.open(file) or { panic(err) }
defer {
f.close()
}
mut r := io.new_buffered_reader(reader: io.make_reader(f), cap: cap)
for {
l := r.read_line() or { break }
lines << l
// println('Line: $l')
}
assert lines.len > 0
assert r.end_of_stream == true
println('------------------------------------------------ cap: ${cap:6}; read: ${lines.len:3} lines')
return lines
}
fn test_file_reader() {
for cap := 64; cap <= 10000; cap += 256 {
lines := read_file(@FILE, cap)
assert lines.last() == '// my last line'
}
}
// my last line