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`
 | ||||
| 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) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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() | ||||
| } | ||||
|  |  | |||
|  | @ -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) | ||||
| 	} | ||||
|  |  | |||
|  | @ -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)) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue