os: simplify file.c.v using cross platform f.seek/2 calls, add EINTR handling to f.write_full_buffer/2 (fix #25107)

This commit is contained in:
Delyan Angelov 2025-08-14 21:54:16 +03:00
parent bbb61ab368
commit c4ef79d146
No known key found for this signature in database
GPG key ID: 66886C0F12D595ED

View file

@ -105,23 +105,20 @@ pub fn open_file(path string, mode string, options ...int) !File {
if fd == -1 { if fd == -1 {
return error(posix_get_error_msg(C.errno)) return error(posix_get_error_msg(C.errno))
} }
cfile := C.fdopen(fd, &char(mode.str)) mut cfile := C.fdopen(fd, &char(mode.str))
if isnil(cfile) { if isnil(cfile) {
return error('Failed to open or create file "${path}"') return error('Failed to open or create file "${path}"')
} }
if seek_to_end { mut res := File{
// ensure appending will work, even on bsd/macos systems:
$if windows {
C._fseeki64(cfile, 0, C.SEEK_END)
} $else {
C.fseeko(cfile, 0, C.SEEK_END)
}
}
return File{
cfile: cfile cfile: cfile
fd: fd fd: fd
is_opened: true is_opened: true
} }
if seek_to_end {
// ensure appending will work, even on bsd/macos systems:
res.seek(0, .end) or {}
}
return res
} }
// open tries to open a file from a given path for reading. // open tries to open a file from a given path for reading.
@ -304,35 +301,13 @@ pub fn (mut f File) write_to(pos u64, buf []u8) !int {
if !f.is_opened { if !f.is_opened {
return error_file_not_opened() return error_file_not_opened()
} }
$if x64 { f.seek(pos, .start) or {}
$if windows { res := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
C._fseeki64(f.cfile, pos, C.SEEK_SET) if res == 0 && buf.len != 0 {
res := int(C.fwrite(buf.data, 1, buf.len, f.cfile)) return error('0 bytes written')
if res == 0 && buf.len != 0 {
return error('0 bytes written')
}
C._fseeki64(f.cfile, 0, C.SEEK_END)
return res
} $else {
C.fseeko(f.cfile, pos, C.SEEK_SET)
res := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
if res == 0 && buf.len != 0 {
return error('0 bytes written')
}
C.fseeko(f.cfile, 0, C.SEEK_END)
return res
}
} }
$if x32 { f.seek(0, .end) or {}
C.fseek(f.cfile, pos, C.SEEK_SET) return res
res := int(C.fwrite(buf.data, 1, buf.len, f.cfile))
if res == 0 && buf.len != 0 {
return error('0 bytes written')
}
C.fseek(f.cfile, 0, C.SEEK_END)
return res
}
return error('Could not write to file')
} }
// write_ptr writes `size` bytes to the file, starting from the address in `data`. // write_ptr writes `size` bytes to the file, starting from the address in `data`.
@ -358,9 +333,17 @@ pub fn (mut f File) write_full_buffer(buffer voidptr, buffer_len usize) ! {
mut remaining_bytes := i64(buffer_len) mut remaining_bytes := i64(buffer_len)
for remaining_bytes > 0 { for remaining_bytes > 0 {
unsafe { unsafe {
C.errno = 0
x := i64(C.fwrite(ptr, 1, remaining_bytes, f.cfile)) x := i64(C.fwrite(ptr, 1, remaining_bytes, f.cfile))
cerror := C.errno
ptr += x ptr += x
remaining_bytes -= x remaining_bytes -= x
if cerror != 0 {
if cerror == C.EINTR {
continue
}
return error(posix_get_error_msg(cerror))
}
if x <= 0 { if x <= 0 {
return error('C.fwrite returned 0') return error('C.fwrite returned 0')
} }
@ -374,26 +357,10 @@ pub fn (mut f File) write_full_buffer(buffer voidptr, buffer_len usize) ! {
// pointers to it, it will cause your programs to segfault. // pointers to it, it will cause your programs to segfault.
@[unsafe] @[unsafe]
pub fn (mut f File) write_ptr_at(data voidptr, size int, pos u64) int { pub fn (mut f File) write_ptr_at(data voidptr, size int, pos u64) int {
$if x64 { f.seek(pos, .start) or {}
$if windows { res := int(C.fwrite(data, 1, size, f.cfile))
C._fseeki64(f.cfile, pos, C.SEEK_SET) f.seek(0, .end) or {}
res := int(C.fwrite(data, 1, size, f.cfile)) return res
C._fseeki64(f.cfile, 0, C.SEEK_END)
return res
} $else {
C.fseeko(f.cfile, pos, C.SEEK_SET)
res := int(C.fwrite(data, 1, size, f.cfile))
C.fseeko(f.cfile, 0, C.SEEK_END)
return res
}
}
$if x32 {
C.fseek(f.cfile, pos, C.SEEK_SET)
res := int(C.fwrite(data, 1, size, f.cfile))
C.fseek(f.cfile, 0, C.SEEK_END)
return res
}
return 0
} }
// **************************** Read ops *************************** // **************************** Read ops ***************************
@ -485,34 +452,13 @@ pub fn (f &File) read_bytes_into(pos u64, mut buf []u8) !int {
if buf.len == 0 { if buf.len == 0 {
return error(@FN + ': `buf.len` == 0') return error(@FN + ': `buf.len` == 0')
} }
$if x64 { // Note: fseek errors if pos == os.file_size, which we accept
$if windows { unsafe { f.seek(pos, .start) or {} }
// Note: fseek errors if pos == os.file_size, which we accept nbytes := fread(buf.data, 1, buf.len, f.cfile)!
C._fseeki64(f.cfile, pos, C.SEEK_SET) $if debug {
nbytes := fread(buf.data, 1, buf.len, f.cfile)! unsafe { f.seek(0, end) or {} }
$if debug {
C._fseeki64(f.cfile, 0, C.SEEK_SET)
}
return nbytes
} $else {
C.fseeko(f.cfile, pos, C.SEEK_SET)
// TODO(alex): require casts for voidptrs? &C.FILE(f.cfile)
nbytes := fread(buf.data, 1, buf.len, f.cfile)!
$if debug {
C.fseeko(f.cfile, 0, C.SEEK_SET)
}
return nbytes
}
} }
$if x32 { return nbytes
C.fseek(f.cfile, pos, C.SEEK_SET)
nbytes := fread(buf.data, 1, buf.len, f.cfile)!
$if debug {
C.fseek(f.cfile, 0, C.SEEK_SET)
}
return nbytes
}
return error('Could not read file')
} }
// read_from implements the RandomReader interface. // read_from implements the RandomReader interface.
@ -520,22 +466,9 @@ pub fn (f &File) read_from(pos u64, mut buf []u8) !int {
if buf.len == 0 { if buf.len == 0 {
return 0 return 0
} }
$if x64 { unsafe { f.seek(pos, .start) or {} }
$if windows { nbytes := fread(buf.data, 1, buf.len, f.cfile)!
C._fseeki64(f.cfile, pos, C.SEEK_SET) return nbytes
} $else {
C.fseeko(f.cfile, pos, C.SEEK_SET)
}
nbytes := fread(buf.data, 1, buf.len, f.cfile)!
return nbytes
}
$if x32 {
C.fseek(f.cfile, pos, C.SEEK_SET)
nbytes := fread(buf.data, 1, buf.len, f.cfile)!
return nbytes
}
return error('Could not read file')
} }
// read_into_ptr reads at most `max_size` bytes from the file and writes it into ptr. // read_into_ptr reads at most `max_size` bytes from the file and writes it into ptr.
@ -602,23 +535,9 @@ pub fn (mut f File) read_struct_at[T](mut t T, pos u64) ! {
if tsize == 0 { if tsize == 0 {
return error_size_of_type_0() return error_size_of_type_0()
} }
mut nbytes := 0 f.seek(pos, .start) or {}
$if x64 { nbytes := fread(t, 1, tsize, f.cfile)!
$if windows { f.seek(0, .end) or {}
C._fseeki64(f.cfile, pos, C.SEEK_SET)
nbytes = fread(t, 1, tsize, f.cfile)!
C._fseeki64(f.cfile, 0, C.SEEK_END)
} $else {
C.fseeko(f.cfile, pos, C.SEEK_SET)
nbytes = fread(t, 1, tsize, f.cfile)!
C.fseeko(f.cfile, 0, C.SEEK_END)
}
}
$if x32 {
C.fseek(f.cfile, pos, C.SEEK_SET)
nbytes = fread(t, 1, tsize, f.cfile)!
C.fseek(f.cfile, 0, C.SEEK_END)
}
if nbytes != tsize { if nbytes != tsize {
return error_with_code('incomplete struct read', nbytes) return error_with_code('incomplete struct read', nbytes)
} }
@ -650,37 +569,10 @@ pub fn (mut f File) read_raw_at[T](pos u64) !T {
if tsize == 0 { if tsize == 0 {
return error_size_of_type_0() return error_size_of_type_0()
} }
mut nbytes := 0
mut t := T{} mut t := T{}
$if x64 { f.seek(pos, .start)!
$if windows { nbytes := fread(&t, 1, tsize, f.cfile)!
if C._fseeki64(f.cfile, pos, C.SEEK_SET) != 0 { f.seek(0, .end)!
return error(posix_get_error_msg(C.errno))
}
nbytes = fread(&t, 1, tsize, f.cfile)!
if C._fseeki64(f.cfile, 0, C.SEEK_END) != 0 {
return error(posix_get_error_msg(C.errno))
}
} $else {
if C.fseeko(f.cfile, pos, C.SEEK_SET) != 0 {
return error(posix_get_error_msg(C.errno))
}
nbytes = fread(&t, 1, tsize, f.cfile)!
if C.fseeko(f.cfile, 0, C.SEEK_END) != 0 {
return error(posix_get_error_msg(C.errno))
}
}
}
$if x32 {
if C.fseek(f.cfile, pos, C.SEEK_SET) != 0 {
return error(posix_get_error_msg(C.errno))
}
nbytes = fread(&t, 1, tsize, f.cfile)!
if C.fseek(f.cfile, 0, C.SEEK_END) != 0 {
return error(posix_get_error_msg(C.errno))
}
}
if nbytes != tsize { if nbytes != tsize {
return error_with_code('incomplete struct read', nbytes) return error_with_code('incomplete struct read', nbytes)
} }
@ -711,34 +603,13 @@ pub fn (mut f File) write_struct_at[T](t &T, pos u64) ! {
if !f.is_opened { if !f.is_opened {
return error_file_not_opened() return error_file_not_opened()
} }
tsize := int(sizeof(T)) tsize := usize(sizeof(T))
if tsize == 0 { if tsize == 0 {
return error_size_of_type_0() return error_size_of_type_0()
} }
C.errno = 0 f.seek(pos, .start) or {}
mut nbytes := 0 unsafe { f.write_full_buffer(t, tsize)! }
$if x64 { f.seek(0, .end) or {}
$if windows {
C._fseeki64(f.cfile, pos, C.SEEK_SET)
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
C._fseeki64(f.cfile, 0, C.SEEK_END)
} $else {
C.fseeko(f.cfile, pos, C.SEEK_SET)
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
C.fseeko(f.cfile, 0, C.SEEK_END)
}
}
$if x32 {
C.fseek(f.cfile, pos, C.SEEK_SET)
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
C.fseek(f.cfile, 0, C.SEEK_END)
}
if C.errno != 0 {
return error(posix_get_error_msg(C.errno))
}
if nbytes != tsize {
return error_with_code('incomplete struct write', nbytes)
}
} }
// TODO: `write_raw[_at]` implementations are copy-pasted from `write_struct[_at]` // TODO: `write_raw[_at]` implementations are copy-pasted from `write_struct[_at]`
@ -748,18 +619,11 @@ pub fn (mut f File) write_raw[T](t &T) ! {
if !f.is_opened { if !f.is_opened {
return error_file_not_opened() return error_file_not_opened()
} }
tsize := int(sizeof(T)) tsize := usize(sizeof(T))
if tsize == 0 { if tsize == 0 {
return error_size_of_type_0() return error_size_of_type_0()
} }
C.errno = 0 unsafe { f.write_full_buffer(t, tsize)! }
nbytes := int(C.fwrite(t, 1, tsize, f.cfile))
if C.errno != 0 {
return error(posix_get_error_msg(C.errno))
}
if nbytes != tsize {
return error_with_code('incomplete struct write', nbytes)
}
} }
// write_raw_at writes a single instance of type `T` starting at file byte offset `pos`. // write_raw_at writes a single instance of type `T` starting at file byte offset `pos`.
@ -767,53 +631,13 @@ pub fn (mut f File) write_raw_at[T](t &T, pos u64) ! {
if !f.is_opened { if !f.is_opened {
return error_file_not_opened() return error_file_not_opened()
} }
tsize := int(sizeof(T)) tsize := usize(sizeof(T))
if tsize == 0 { if tsize == 0 {
return error_size_of_type_0() return error_size_of_type_0()
} }
mut nbytes := 0 f.seek(pos, .start)!
unsafe { f.write_full_buffer(t, tsize)! }
$if x64 { f.seek(0, .end)!
$if windows {
if C._fseeki64(f.cfile, pos, C.SEEK_SET) != 0 {
return error(posix_get_error_msg(C.errno))
}
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
if C.errno != 0 {
return error(posix_get_error_msg(C.errno))
}
if C._fseeki64(f.cfile, 0, C.SEEK_END) != 0 {
return error(posix_get_error_msg(C.errno))
}
} $else {
if C.fseeko(f.cfile, pos, C.SEEK_SET) != 0 {
return error(posix_get_error_msg(C.errno))
}
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
if C.errno != 0 {
return error(posix_get_error_msg(C.errno))
}
if C.fseeko(f.cfile, 0, C.SEEK_END) != 0 {
return error(posix_get_error_msg(C.errno))
}
}
}
$if x32 {
if C.fseek(f.cfile, pos, C.SEEK_SET) != 0 {
return error(posix_get_error_msg(C.errno))
}
nbytes = int(C.fwrite(t, 1, tsize, f.cfile))
if C.errno != 0 {
return error(posix_get_error_msg(C.errno))
}
if C.fseek(f.cfile, 0, C.SEEK_END) != 0 {
return error(posix_get_error_msg(C.errno))
}
}
if nbytes != tsize {
return error_with_code('incomplete struct write', nbytes)
}
} }
pub enum SeekMode { pub enum SeekMode {