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() {
// 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
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
mut r := io.new_buffered_reader(reader: io.make_reader(conn))
for {
l := r.read_line() or {
break
}
l := r.read_line() or { break }
println('$l')
// 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
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
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
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
@ -26,6 +28,7 @@ pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader {
reader: o.reader
buf: []byte{len: o.cap, cap: o.cap}
offset: 0
mfails: o.retries
}
return r
}
@ -59,6 +62,17 @@ fn (mut r BufferedReader) fill_buffer() bool {
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
}

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