os: add `(read|write)_raw[_at]` to File (#9171)

pull/9238/head
Enzo 2021-03-10 17:45:12 +01:00 committed by GitHub
parent 8d84206a8c
commit 99abd46ac9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 241 additions and 38 deletions

View File

@ -358,12 +358,60 @@ pub fn (mut f File) read_struct_at<T>(mut t T, pos int) ? {
}
}
// read_raw reads and returns a single instance of type `T`
pub fn (mut f File) read_raw<T>() ?T {
if !f.is_opened {
return none
}
tsize := int(sizeof(T))
if tsize == 0 {
return none
}
C.errno = 0
mut t := T{}
nbytes := int(C.fread(&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 read', nbytes)
}
return t
}
// read_raw_at reads and returns a single instance of type `T` starting at file byte offset `pos`
pub fn (mut f File) read_raw_at<T>(pos int) ?T {
if !f.is_opened {
return none
}
tsize := int(sizeof(T))
if tsize == 0 {
return none
}
C.errno = 0
if C.fseek(f.cfile, pos, C.SEEK_SET) != 0 {
return error(posix_get_error_msg(C.errno))
}
mut t := T{}
nbytes := int(C.fread(&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 read', nbytes)
}
return t
}
// write_struct writes a single struct of type `T`
pub fn (mut f File) write_struct<T>(t &T) ? {
if !f.is_opened {
return error('file is not opened')
}
tsize := int(sizeof(*t))
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
}
@ -382,7 +430,7 @@ pub fn (mut f File) write_struct_at<T>(t &T, pos int) ? {
if !f.is_opened {
return error('file is not opened')
}
tsize := int(sizeof(*t))
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
}
@ -397,3 +445,48 @@ pub fn (mut f File) write_struct_at<T>(t &T, pos int) ? {
return error_with_code('incomplete struct write', nbytes)
}
}
// TODO `write_raw[_at]` implementations are copy-pasted from `write_struct[_at]`
// write_raw writes a single instance of type `T`
pub fn (mut f File) write_raw<T>(t &T) ? {
if !f.is_opened {
return error('file is not opened')
}
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
}
C.errno = 0
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`
pub fn (mut f File) write_raw_at<T>(t &T, pos int) ? {
if !f.is_opened {
return error('file is not opened')
}
tsize := int(sizeof(T))
if tsize == 0 {
return error('struct size is 0')
}
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)
}
}

View File

@ -18,36 +18,54 @@ struct Extended_Point {
i f64
}
const unit_point = Point{1.0, 1.0, 1.0}
enum Color {
red
green
blue
}
const tfolder = os.join_path(os.temp_dir(), 'os_file_test')
[flag]
enum Permissions {
read
write
execute
}
const tfile = os.join_path(tfolder, 'test_file')
const (
unit_point = Point{1.0, 1.0, 1.0}
another_point = Point{0.25, 2.25, 6.25}
extended_point = Extended_Point{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}
another_byte = byte(123)
another_color = Color.red
another_permission = Permissions.read | .write
)
const another_point = Point{0.25, 2.25, 6.25}
const (
tfolder = os.join_path(os.temp_dir(), 'os_file_test')
tfile = os.join_path(tfolder, 'test_file')
)
const extended_point = Extended_Point{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}
fn testsuite_begin() {
fn testsuite_begin() ? {
os.rmdir_all(tfolder) or {}
assert !os.is_dir(tfolder)
os.mkdir_all(tfolder) or { panic(err) }
os.mkdir_all(tfolder) ?
os.chdir(tfolder)
assert os.is_dir(tfolder)
}
fn testsuite_end() {
fn testsuite_end() ? {
os.chdir(os.wd_at_startup)
os.rmdir_all(tfolder) or { panic(err) }
os.rmdir_all(tfolder) ?
assert !os.is_dir(tfolder)
}
fn test_write_struct() {
fn test_write_struct() ? {
os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w'
size_of_point := int(sizeof(Point))
mut f := os.open_file(tfile, 'w') or { panic(err) }
f.write_struct(another_point) or { panic(err) }
mut f := os.open_file(tfile, 'w') ?
f.write_struct(another_point) ?
f.close()
x := os.read_file(tfile) or { panic(err) }
x := os.read_file(tfile) ?
y := unsafe { byteptr(memdup(&another_point, size_of_point)).vstring_with_len(size_of_point) }
assert x == y
$if debug {
@ -56,41 +74,133 @@ fn test_write_struct() {
}
}
fn test_read_struct() {
mut f := os.open_file(tfile, 'w') or { panic(err) }
f.write_struct(another_point) or { panic(err) }
fn test_write_struct_at() ? {
mut f := os.open_file(tfile, 'w') ?
f.write_struct(extended_point) ?
f.write_struct_at(another_point, 3) ?
f.close()
f = os.open_file(tfile, 'r') or { panic(err) }
f = os.open_file(tfile, 'r') ?
mut p := Point{}
f.read_struct(mut p) or { panic(err) }
f.read_struct_at(mut p, 3) ?
f.close()
assert p == another_point
}
fn test_read_struct_at() {
mut f := os.open_file(tfile, 'w') or { panic(err) }
f.write([byte(1), 2, 3]) or { panic(err) }
f.write_struct(another_point) or { panic(err) }
fn test_read_struct() ? {
mut f := os.open_file(tfile, 'w') ?
f.write_struct(another_point) ?
f.close()
f = os.open_file(tfile, 'r') or { panic(err) }
f = os.open_file(tfile, 'r') ?
mut p := Point{}
f.read_struct_at(mut p, 3) or { panic(err) }
f.read_struct(mut p) ?
f.close()
assert p == another_point
}
fn test_write_struct_at() {
mut f := os.open_file(tfile, 'w') or { panic(err) }
f.write_struct(extended_point) or { panic(err) }
f.write_struct_at(another_point, 3) or { panic(err) }
fn test_read_struct_at() ? {
mut f := os.open_file(tfile, 'w') ?
f.write([byte(1), 2, 3]) ?
f.write_struct(another_point) ?
f.close()
f = os.open_file(tfile, 'r') or { panic(err) }
f = os.open_file(tfile, 'r') ?
mut p := Point{}
f.read_struct_at(mut p, 3) or { panic(err) }
f.read_struct_at(mut p, 3) ?
f.close()
assert p == another_point
}
fn test_write_raw() ? {
os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w'
size_of_point := int(sizeof(Point))
mut f := os.open_file(tfile, 'w') ?
f.write_raw(another_point) ?
f.close()
x := os.read_file(tfile) ?
y := unsafe { byteptr(memdup(&another_point, size_of_point)).vstring_with_len(size_of_point) }
assert x == y
$if debug {
eprintln(x.bytes())
eprintln(y.bytes())
}
}
fn test_write_raw_at() ? {
mut f := os.open_file(tfile, 'w') ?
f.write_raw(extended_point) ?
f.write_raw_at(another_point, 3) ?
f.close()
f = os.open_file(tfile, 'r') ?
mut p := Point{}
f.read_struct_at(mut p, 3) ?
f.close()
assert p == another_point
}
fn test_write_raw_at_negative_pos() ? {
mut f := os.open_file(tfile, 'w') ?
if _ := f.write_raw_at(another_point, -1) {
assert false
}
f.write_raw_at(another_point, -234) or { assert err.msg == 'Invalid argument' }
f.close()
}
fn test_read_raw() ? {
mut f := os.open_file(tfile, 'w') ?
f.write_raw(another_point) ?
f.write_raw(another_byte) ?
f.write_raw(another_color) ?
f.write_raw(another_permission) ?
f.close()
f = os.open_file(tfile, 'r') ?
p := f.read_raw<Point>() ?
b := f.read_raw<byte>() ?
c := f.read_raw<Color>() ?
x := f.read_raw<Permissions>() ?
f.close()
assert p == another_point
assert b == another_byte
assert c == another_color
assert x == another_permission
}
fn test_read_raw_at() ? {
mut f := os.open_file(tfile, 'w') ?
f.write([byte(1), 2, 3]) ?
f.write_raw(another_point) ?
f.write_raw(another_byte) ?
f.write_raw(another_color) ?
f.write_raw(another_permission) ?
f.close()
f = os.open_file(tfile, 'r') ?
mut at := 3
p := f.read_raw_at<Point>(at) ?
at += int(sizeof(Point))
b := f.read_raw_at<byte>(at) ?
at += int(sizeof(byte))
c := f.read_raw_at<Color>(at) ?
at += int(sizeof(Color))
x := f.read_raw_at<Permissions>(at) ?
at += int(sizeof(Permissions))
f.close()
assert p == another_point
assert b == another_byte
assert c == another_color
assert x == another_permission
}
fn test_read_raw_at_negative_pos() ? {
mut f := os.open_file(tfile, 'r') ?
if _ := f.read_raw_at<Point>(-1) {
assert false
}
f.read_raw_at<Point>(-234) or { assert err.msg == 'Invalid argument' }
f.close()
}

View File

@ -138,11 +138,11 @@ pub fn rmdir_all(path string) ? {
for item in items {
fullpath := join_path(path, item)
if is_dir(fullpath) {
rmdir_all(fullpath) or { ret_err = err }
rmdir_all(fullpath) or { ret_err = err.msg }
}
rm(fullpath) or { ret_err = err }
rm(fullpath) or { ret_err = err.msg }
}
rmdir(path) or { ret_err = err }
rmdir(path) or { ret_err = err.msg }
if ret_err.len > 0 {
return error(ret_err)
}

View File

@ -382,7 +382,7 @@ pub fn rmdir(path string) ? {
rc := C.RemoveDirectory(path.to_wide())
if rc == 0 {
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya - 0 is failure
return error('Failed to remove "$path"')
return error('Failed to remove "$path": ' + posix_get_error_msg(C.errno))
}
} $else {
rc := C.rmdir(charptr(path.str))