mirror of
https://github.com/vlang/v.git
synced 2025-09-13 22:42:26 +03:00
os: add File.set_buffer and related helpers + tests (#20661)
This commit is contained in:
parent
2a68e2b7c9
commit
7f5f92ddd4
2 changed files with 129 additions and 0 deletions
42
vlib/os/file_buffering.c.v
Normal file
42
vlib/os/file_buffering.c.v
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
module os
|
||||||
|
|
||||||
|
fn C.setvbuf(stream &C.FILE, buffer &char, mode int, size usize) int
|
||||||
|
|
||||||
|
// FileBufferMode describes the available buffering modes for an os.File: unbuffered, line buffered and block/fully buffered.
|
||||||
|
// Normally all files are block buffered. If a stream refers to a terminal (as stdout normally does), it is line buffered.
|
||||||
|
// The standard error stream stderr is always unbuffered by default.
|
||||||
|
pub enum FileBufferMode {
|
||||||
|
fully_buffered = C._IOFBF // many characters are saved up and written as a block
|
||||||
|
line_buffered = C._IOLBF // characters are saved up until a newline is output or input is read from any stream attached to a terminal device (typically stdin)
|
||||||
|
not_buffered = C._IONBF // information appears on the destination file or terminal as soon as it is written
|
||||||
|
}
|
||||||
|
|
||||||
|
// setvbuf sets the buffer and buffering mode for the given file `f`. See also os.FileBufferMode.
|
||||||
|
// It returns 0 on success. It returns nonzero on failure (the mode is invalid, or the request cannot be honored).
|
||||||
|
// Except for unbuffered files, the buffer argument should point to a buffer at least size bytes long; this buffer will be used instead of the current buffer.
|
||||||
|
// If the argument buffer is nil, only the mode is affected; a new buffer will be allocated on the next read or write operation.
|
||||||
|
// Note: make sure, that the space, that buffer points to (when != 0), still exists by the time the file stream is closed, which also happens at program termination.
|
||||||
|
// Note: f.setvbuf() may be used only after opening a file stream, and before any other operations have been performed on it.
|
||||||
|
@[unsafe]
|
||||||
|
pub fn (mut f File) setvbuf(buffer &char, mode FileBufferMode, size usize) int {
|
||||||
|
return C.setvbuf(f.cfile, buffer, int(mode), size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set_buffer sets the buffer for the file, and the file buffering mode (see also os.FileBufferMode).
|
||||||
|
// Unlike File.setvbuf, it allows you to pass an existing V []u8 array directly.
|
||||||
|
// Note: f.set_buffer() may be used only after opening a file stream, and before any other operations have been performed on it.
|
||||||
|
pub fn (mut f File) set_buffer(mut buffer []u8, mode FileBufferMode) int {
|
||||||
|
return unsafe { f.setvbuf(&char(buffer.data), mode, usize(buffer.len)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// set_line_buffered sets the file buffering mode to FileBufferMode.line_buffered.
|
||||||
|
// Note: f.set_line_buffered() may be used only after opening a file stream, and before any other operations have been performed on it.
|
||||||
|
pub fn (mut f File) set_line_buffered() {
|
||||||
|
unsafe { f.setvbuf(&char(nil), .line_buffered, usize(0)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// set_unbuffered sets the file buffering mode to FileBufferMode.not_buffered.
|
||||||
|
// Note: f.set_unbuffered() may be used only after opening a file stream, and before any other operations have been performed on it.
|
||||||
|
pub fn (mut f File) set_unbuffered() {
|
||||||
|
unsafe { f.setvbuf(&char(nil), .not_buffered, usize(0)) }
|
||||||
|
}
|
87
vlib/os/file_buffering_test.v
Normal file
87
vlib/os/file_buffering_test.v
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
const tfolder = os.join_path(os.vtmp_dir(), 'tests', 'os_file_buffering_test')
|
||||||
|
|
||||||
|
fn testsuite_begin() {
|
||||||
|
os.rmdir_all(tfolder) or {}
|
||||||
|
assert !os.is_dir(tfolder)
|
||||||
|
os.mkdir_all(tfolder)!
|
||||||
|
os.chdir(tfolder)!
|
||||||
|
assert os.is_dir(tfolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testsuite_end() {
|
||||||
|
os.rmdir_all(tfolder) or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_set_buffer_line_buffered() {
|
||||||
|
dump(@LOCATION)
|
||||||
|
mut buf := []u8{len: 25}
|
||||||
|
dump(buf)
|
||||||
|
mut wfile := os.open_file('text.txt', 'wb', 0o666)!
|
||||||
|
wfile.set_buffer(mut buf, .line_buffered)
|
||||||
|
wfile.write_string('----------------------------------\n')!
|
||||||
|
for line in ['hello\n', 'world\n', 'hi\n'] {
|
||||||
|
wfile.write_string(line)!
|
||||||
|
wfile.flush()
|
||||||
|
dump(buf)
|
||||||
|
print(buf.bytestr())
|
||||||
|
// assert buf.bytestr().contains(line) // this works on GLIBC, but fails on MUSL.
|
||||||
|
unsafe { buf.reset() }
|
||||||
|
}
|
||||||
|
wfile.close()
|
||||||
|
//
|
||||||
|
content := os.read_lines('text.txt')!
|
||||||
|
dump(content)
|
||||||
|
assert content == ['----------------------------------', 'hello', 'world', 'hi']
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_set_buffer_fully_buffered() {
|
||||||
|
dump(@LOCATION)
|
||||||
|
mut buf := []u8{len: 30}
|
||||||
|
dump(buf)
|
||||||
|
mut wfile := os.open_file('text.txt', 'wb', 0o666)!
|
||||||
|
wfile.set_buffer(mut buf, .fully_buffered)
|
||||||
|
// Ubuntu GLIBC 2.31 seems to not use the buffer for the first write call, but it does write to the buffer first for the subsequent ones.
|
||||||
|
// MUSL (detecting the MUSL version is deliberately made hard by its authors, because of course it is :-( ...), will skip the first 8 bytes
|
||||||
|
// of the buffer, and write everything after those.
|
||||||
|
wfile.write_string('S')!
|
||||||
|
wfile.write_string('---\n')!
|
||||||
|
dump(buf)
|
||||||
|
for line in ['hello\n', 'world\n', 'hi\n'] {
|
||||||
|
wfile.write_string(line)!
|
||||||
|
dump(buf)
|
||||||
|
// print(buf.bytestr())
|
||||||
|
}
|
||||||
|
wfile.close()
|
||||||
|
dump(buf)
|
||||||
|
// assert buf.bytestr().starts_with('---\nhello\nworld\nhi\n') // works on GLIBC, fails on MUSL
|
||||||
|
assert buf.bytestr().contains('---\nhello\nworld\n')
|
||||||
|
//
|
||||||
|
content := os.read_lines('text.txt')!
|
||||||
|
dump(content)
|
||||||
|
assert content == ['S---', 'hello', 'world', 'hi']
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_set_unbuffered() {
|
||||||
|
dump(@LOCATION)
|
||||||
|
mut buf := []u8{len: 30}
|
||||||
|
dump(buf)
|
||||||
|
mut wfile := os.open_file('text.txt', 'wb', 0o666)!
|
||||||
|
wfile.set_buffer(mut buf, .not_buffered)
|
||||||
|
wfile.write_string('S')!
|
||||||
|
wfile.write_string('---\n')!
|
||||||
|
dump(buf)
|
||||||
|
for line in ['hello\n', 'world\n', 'hi\n'] {
|
||||||
|
wfile.write_string(line)!
|
||||||
|
dump(buf)
|
||||||
|
// print(buf.bytestr())
|
||||||
|
}
|
||||||
|
wfile.close()
|
||||||
|
// dump(buf.bytestr())
|
||||||
|
assert buf.all(it == 0)
|
||||||
|
//
|
||||||
|
content := os.read_lines('text.txt')!
|
||||||
|
dump(content)
|
||||||
|
assert content == ['S---', 'hello', 'world', 'hi']
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue