os: implement File.read_bytes_into_newline method for reading into a buffer, line by line (#10129)
parent
87ded1784e
commit
04ea2824d3
|
@ -17,6 +17,8 @@ fn C.fseeko(voidptr, u64, int) int
|
||||||
|
|
||||||
fn C._fseeki64(voidptr, u64, int) int
|
fn C._fseeki64(voidptr, u64, int) int
|
||||||
|
|
||||||
|
fn C.getc(voidptr) int
|
||||||
|
|
||||||
// open_file can be used to open or create a file with custom flags and permissions and returns a `File` object.
|
// open_file can be used to open or create a file with custom flags and permissions and returns a `File` object.
|
||||||
pub fn open_file(path string, mode string, options ...int) ?File {
|
pub fn open_file(path string, mode string, options ...int) ?File {
|
||||||
mut flags := 0
|
mut flags := 0
|
||||||
|
@ -362,6 +364,44 @@ pub fn (f &File) read_bytes_at(size int, pos u64) []byte {
|
||||||
return arr[0..nreadbytes]
|
return arr[0..nreadbytes]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read_bytes_into_newline reads from the beginning of the file into the provided buffer.
|
||||||
|
// Each consecutive call on the same file continues reading where it previously ended.
|
||||||
|
// A read call is either stopped, if the buffer is full, a newline was read or EOF.
|
||||||
|
pub fn (f &File) read_bytes_into_newline(mut buf []byte) ?int {
|
||||||
|
if buf.len == 0 {
|
||||||
|
panic(@FN + ': `buf.len` == 0')
|
||||||
|
}
|
||||||
|
newline := 10
|
||||||
|
mut c := 0
|
||||||
|
mut buf_ptr := 0
|
||||||
|
mut nbytes := 0
|
||||||
|
|
||||||
|
for (buf_ptr < buf.len) {
|
||||||
|
c = C.getc(f.cfile)
|
||||||
|
match c {
|
||||||
|
C.EOF {
|
||||||
|
if C.feof(f.cfile) != 0 {
|
||||||
|
return nbytes
|
||||||
|
}
|
||||||
|
if C.ferror(f.cfile) != 0 {
|
||||||
|
return error('file read error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newline {
|
||||||
|
buf[buf_ptr] = byte(c)
|
||||||
|
nbytes++
|
||||||
|
return nbytes
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf[buf_ptr] = byte(c)
|
||||||
|
buf_ptr++
|
||||||
|
nbytes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nbytes
|
||||||
|
}
|
||||||
|
|
||||||
// read_bytes_into fills `buf` with bytes at the given position in the file.
|
// read_bytes_into fills `buf` with bytes at the given position in the file.
|
||||||
// `buf` *must* have length greater than zero.
|
// `buf` *must* have length greater than zero.
|
||||||
// Returns the number of read bytes, or an error.
|
// Returns the number of read bytes, or an error.
|
||||||
|
|
|
@ -59,6 +59,67 @@ fn testsuite_end() ? {
|
||||||
assert !os.is_dir(tfolder)
|
assert !os.is_dir(tfolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test_read_bytes_into_newline_text tests reading text from a file with newlines.
|
||||||
|
// This test simulates reading a larger text file step by step into a buffer and
|
||||||
|
// returning on each newline, even before the buffer is full, and reaching EOF before
|
||||||
|
// the buffer is completely filled.
|
||||||
|
fn test_read_bytes_into_newline_text() ? {
|
||||||
|
mut f := os.open_file(tfile, 'w') ?
|
||||||
|
f.write_string('Hello World!\nGood\r morning.') ?
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = os.open_file(tfile, 'r') ?
|
||||||
|
mut buf := []byte{len: 8}
|
||||||
|
|
||||||
|
n0 := f.read_bytes_into_newline(mut buf) ?
|
||||||
|
assert n0 == 8
|
||||||
|
|
||||||
|
n1 := f.read_bytes_into_newline(mut buf) ?
|
||||||
|
assert n1 == 5
|
||||||
|
|
||||||
|
n2 := f.read_bytes_into_newline(mut buf) ?
|
||||||
|
assert n2 == 8
|
||||||
|
|
||||||
|
n3 := f.read_bytes_into_newline(mut buf) ?
|
||||||
|
assert n3 == 6
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// test_read_bytes_into_newline_binary tests reading a binary file with NUL bytes.
|
||||||
|
// This test simulates the scenario when a byte stream is read and a newline byte
|
||||||
|
// appears in that stream and an EOF occurs before the buffer is full.
|
||||||
|
fn test_read_bytes_into_newline_binary() ? {
|
||||||
|
os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w'
|
||||||
|
mut bw := []byte{len: 15}
|
||||||
|
bw[9] = 0xff
|
||||||
|
bw[12] = 10 // newline
|
||||||
|
|
||||||
|
n0_bytes := bw[0..10]
|
||||||
|
n1_bytes := bw[10..13]
|
||||||
|
n2_bytes := bw[13..]
|
||||||
|
|
||||||
|
mut f := os.open_file(tfile, 'w') ?
|
||||||
|
f.write(bw) ?
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = os.open_file(tfile, 'r') ?
|
||||||
|
mut buf := []byte{len: 10}
|
||||||
|
|
||||||
|
n0 := f.read_bytes_into_newline(mut buf) ?
|
||||||
|
assert n0 == 10
|
||||||
|
assert buf[..n0] == n0_bytes
|
||||||
|
|
||||||
|
n1 := f.read_bytes_into_newline(mut buf) ?
|
||||||
|
assert n1 == 3
|
||||||
|
assert buf[..n1] == n1_bytes
|
||||||
|
|
||||||
|
n2 := f.read_bytes_into_newline(mut buf) ?
|
||||||
|
assert n2 == 2
|
||||||
|
assert buf[..n2] == n2_bytes
|
||||||
|
f.close()
|
||||||
|
}
|
||||||
|
|
||||||
// test_read_eof_last_read_partial_buffer_fill tests that when reading a file
|
// test_read_eof_last_read_partial_buffer_fill tests that when reading a file
|
||||||
// the end-of-file is detected and results in a none error being returned. This
|
// the end-of-file is detected and results in a none error being returned. This
|
||||||
// test simulates file reading where the end-of-file is reached inside an fread
|
// test simulates file reading where the end-of-file is reached inside an fread
|
||||||
|
|
Loading…
Reference in New Issue