os: add `(read|write)_raw[_at]` to File (#9171)
parent
8d84206a8c
commit
99abd46ac9
|
@ -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`
|
// write_struct writes a single struct of type `T`
|
||||||
pub fn (mut f File) write_struct<T>(t &T) ? {
|
pub fn (mut f File) write_struct<T>(t &T) ? {
|
||||||
if !f.is_opened {
|
if !f.is_opened {
|
||||||
return error('file is not opened')
|
return error('file is not opened')
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(*t))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return error('struct size is 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 {
|
if !f.is_opened {
|
||||||
return error('file is not opened')
|
return error('file is not opened')
|
||||||
}
|
}
|
||||||
tsize := int(sizeof(*t))
|
tsize := int(sizeof(T))
|
||||||
if tsize == 0 {
|
if tsize == 0 {
|
||||||
return error('struct size is 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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,36 +18,54 @@ struct Extended_Point {
|
||||||
i f64
|
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 {}
|
os.rmdir_all(tfolder) or {}
|
||||||
assert !os.is_dir(tfolder)
|
assert !os.is_dir(tfolder)
|
||||||
os.mkdir_all(tfolder) or { panic(err) }
|
os.mkdir_all(tfolder) ?
|
||||||
os.chdir(tfolder)
|
os.chdir(tfolder)
|
||||||
assert os.is_dir(tfolder)
|
assert os.is_dir(tfolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testsuite_end() {
|
fn testsuite_end() ? {
|
||||||
os.chdir(os.wd_at_startup)
|
os.chdir(os.wd_at_startup)
|
||||||
os.rmdir_all(tfolder) or { panic(err) }
|
os.rmdir_all(tfolder) ?
|
||||||
assert !os.is_dir(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))
|
size_of_point := int(sizeof(Point))
|
||||||
mut f := os.open_file(tfile, 'w') or { panic(err) }
|
mut f := os.open_file(tfile, 'w') ?
|
||||||
f.write_struct(another_point) or { panic(err) }
|
f.write_struct(another_point) ?
|
||||||
f.close()
|
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) }
|
y := unsafe { byteptr(memdup(&another_point, size_of_point)).vstring_with_len(size_of_point) }
|
||||||
assert x == y
|
assert x == y
|
||||||
$if debug {
|
$if debug {
|
||||||
|
@ -56,41 +74,133 @@ fn test_write_struct() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_read_struct() {
|
fn test_write_struct_at() ? {
|
||||||
mut f := os.open_file(tfile, 'w') or { panic(err) }
|
mut f := os.open_file(tfile, 'w') ?
|
||||||
f.write_struct(another_point) or { panic(err) }
|
f.write_struct(extended_point) ?
|
||||||
|
f.write_struct_at(another_point, 3) ?
|
||||||
f.close()
|
f.close()
|
||||||
|
f = os.open_file(tfile, 'r') ?
|
||||||
f = os.open_file(tfile, 'r') or { panic(err) }
|
|
||||||
mut p := Point{}
|
mut p := Point{}
|
||||||
f.read_struct(mut p) or { panic(err) }
|
f.read_struct_at(mut p, 3) ?
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
assert p == another_point
|
assert p == another_point
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_read_struct_at() {
|
fn test_read_struct() ? {
|
||||||
mut f := os.open_file(tfile, 'w') or { panic(err) }
|
mut f := os.open_file(tfile, 'w') ?
|
||||||
f.write([byte(1), 2, 3]) or { panic(err) }
|
f.write_struct(another_point) ?
|
||||||
f.write_struct(another_point) or { panic(err) }
|
|
||||||
f.close()
|
f.close()
|
||||||
f = os.open_file(tfile, 'r') or { panic(err) }
|
|
||||||
|
f = os.open_file(tfile, 'r') ?
|
||||||
mut p := Point{}
|
mut p := Point{}
|
||||||
f.read_struct_at(mut p, 3) or { panic(err) }
|
f.read_struct(mut p) ?
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
assert p == another_point
|
assert p == another_point
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_write_struct_at() {
|
fn test_read_struct_at() ? {
|
||||||
mut f := os.open_file(tfile, 'w') or { panic(err) }
|
mut f := os.open_file(tfile, 'w') ?
|
||||||
f.write_struct(extended_point) or { panic(err) }
|
f.write([byte(1), 2, 3]) ?
|
||||||
f.write_struct_at(another_point, 3) or { panic(err) }
|
f.write_struct(another_point) ?
|
||||||
f.close()
|
f.close()
|
||||||
f = os.open_file(tfile, 'r') or { panic(err) }
|
f = os.open_file(tfile, 'r') ?
|
||||||
mut p := Point{}
|
mut p := Point{}
|
||||||
f.read_struct_at(mut p, 3) or { panic(err) }
|
f.read_struct_at(mut p, 3) ?
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
assert p == another_point
|
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()
|
||||||
|
}
|
||||||
|
|
|
@ -138,11 +138,11 @@ pub fn rmdir_all(path string) ? {
|
||||||
for item in items {
|
for item in items {
|
||||||
fullpath := join_path(path, item)
|
fullpath := join_path(path, item)
|
||||||
if is_dir(fullpath) {
|
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 {
|
if ret_err.len > 0 {
|
||||||
return error(ret_err)
|
return error(ret_err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,7 +382,7 @@ pub fn rmdir(path string) ? {
|
||||||
rc := C.RemoveDirectory(path.to_wide())
|
rc := C.RemoveDirectory(path.to_wide())
|
||||||
if rc == 0 {
|
if rc == 0 {
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya - 0 is failure
|
// 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 {
|
} $else {
|
||||||
rc := C.rmdir(charptr(path.str))
|
rc := C.rmdir(charptr(path.str))
|
||||||
|
|
Loading…
Reference in New Issue