From d7dcb47db3664df85e6f87e0c42af12c7e66d7e9 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 22 Jul 2021 07:46:21 +0300 Subject: [PATCH] os: implement File.seek/2 and File.tell/0 --- vlib/os/file.c.v | 58 +++++++++++++++++++++++++++++++++++++++++++++ vlib/os/file_test.v | 28 ++++++++++++++++++++++ vlib/os/os_c.v | 12 +++++----- vlib/os/os_nix.c.v | 5 +--- 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/vlib/os/file.c.v b/vlib/os/file.c.v index adb45b2b50..40feed090d 100644 --- a/vlib/os/file.c.v +++ b/vlib/os/file.c.v @@ -16,6 +16,7 @@ struct FileInfo { fn C.fseeko(&C.FILE, u64, int) int fn C._fseeki64(&C.FILE, u64, int) int +fn C._ftelli64(&C.FILE) i64 fn C.getc(&C.FILE) int @@ -727,3 +728,60 @@ pub fn (mut f File) write_raw_at(t &T, pos u64) ? { return error_with_code('incomplete struct write', nbytes) } } + +pub enum SeekMode { + start + current + end +} + +// seek moves the file cursor (if any) associated with a file +// to a new location, offset `pos` bytes from the origin. The origin +// is dependent on the `mode` and can be: +// .start -> the origin is the start of the file +// .current -> the current position/cursor in the file +// .end -> the end of the file +// If the file is not seek-able, or an error occures, the error will +// be returned to the caller. +// A successful call to the fseek() function clears the end-of-file +// indicator for the file. +pub fn (mut f File) seek(pos i64, mode SeekMode) ? { + if !f.is_opened { + return error_file_not_opened() + } + whence := int(mode) + mut res := 0 + $if x64 { + $if windows { + res = C._fseeki64(f.cfile, pos, whence) + } $else { + res = C.fseeko(f.cfile, pos, whence) + } + } + $if x32 { + res = C.fseek(f.cfile, pos, whence) + } + if res == -1 { + return error(posix_get_error_msg(C.errno)) + } +} + +// tell will return the current offset of the file cursor measured from +// the start of the file, in bytes. It is complementary to seek, i.e. +// you can use the return value as the `pos` parameter to .seek( pos, .start ), +// so that your next read will happen from the same place. +pub fn (f &File) tell() ?i64 { + if !f.is_opened { + return error_file_not_opened() + } + mut pos := i64(0) + $if windows && x64 { + pos = C._ftelli64(f.cfile) + } $else { + pos = C.ftell(f.cfile) + } + if pos == -1 { + return error(posix_get_error_msg(C.errno)) + } + return pos +} diff --git a/vlib/os/file_test.v b/vlib/os/file_test.v index 493dc1220a..0794e3c101 100644 --- a/vlib/os/file_test.v +++ b/vlib/os/file_test.v @@ -331,3 +331,31 @@ fn test_read_raw_at_negative_pos() ? { f.read_raw_at(-234) or { assert err.msg == 'Invalid argument' } f.close() } + +fn test_seek() ? { + 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() + + // println('> ${sizeof(Point)} ${sizeof(byte)} ${sizeof(Color)} ${sizeof(Permissions)}') + f = os.open_file(tfile, 'r') ? + defer { + f.close() + } + f.seek(i64(sizeof(Point)), .start) ? + assert f.tell() ? == sizeof(Point) + b := f.read_raw() ? + + f.seek(i64(sizeof(Color)), .current) ? + x := f.read_raw() ? + + f.seek(-i64(sizeof(Permissions) + sizeof(Color)), .end) ? + assert f.tell() ? == sizeof(Point) + sizeof(byte) + cc := f.read_raw() ? + assert b == another_byte + assert x == another_permission + assert cc == another_color +} diff --git a/vlib/os/os_c.v b/vlib/os/os_c.v index c9d8c3a80a..8275a9f34a 100644 --- a/vlib/os/os_c.v +++ b/vlib/os/os_c.v @@ -13,7 +13,7 @@ fn C.readlink(pathname &char, buf &char, bufsiz size_t) int fn C.getline(voidptr, voidptr, voidptr) int -fn C.ftell(fp voidptr) int +fn C.ftell(fp voidptr) i64 fn C.sigaction(int, voidptr, int) int @@ -83,12 +83,12 @@ pub fn read_bytes(path string) ?[]byte { return error('ftell failed') } C.rewind(fp) - mut res := []byte{len: fsize} + mut res := []byte{len: int(fsize)} nr_read_elements := int(C.fread(res.data, fsize, 1, fp)) if nr_read_elements == 0 && fsize > 0 { return error('fread failed') } - res.trim(nr_read_elements * fsize) + res.trim(nr_read_elements * int(fsize)) return res } @@ -110,7 +110,7 @@ pub fn read_file(path string) ?string { // C.fseek(fp, 0, SEEK_SET) // same as `C.rewind(fp)` below C.rewind(fp) unsafe { - mut str := malloc_noscan(fsize + 1) + mut str := malloc_noscan(int(fsize) + 1) nelements := int(C.fread(str, 1, fsize, fp)) is_eof := int(C.feof(fp)) is_error := int(C.ferror(fp)) @@ -586,7 +586,7 @@ pub fn read_file_array(path string) []T { C.rewind(fp) // read the actual data from the file len := fsize / tsize - buf := unsafe { malloc_noscan(fsize) } + buf := unsafe { malloc_noscan(int(fsize)) } nread := C.fread(buf, tsize, len, fp) C.fclose(fp) return unsafe { @@ -594,7 +594,7 @@ pub fn read_file_array(path string) []T { element_size: tsize data: buf len: int(nread) - cap: len + cap: int(len) } } } diff --git a/vlib/os/os_nix.c.v b/vlib/os/os_nix.c.v index aa2bb3b34b..1a2a26c866 100644 --- a/vlib/os/os_nix.c.v +++ b/vlib/os/os_nix.c.v @@ -304,10 +304,7 @@ pub fn is_dir(path string) bool { return res } */ -/* -pub fn (mut f File) fseek(pos, mode int) { -} -*/ + // mkdir creates a new directory with the specified path. pub fn mkdir(path string) ?bool { if path == '.' {