diff --git a/src/os/file.go b/src/os/file.go index bb7b3f0d..20e56fac 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -107,8 +107,15 @@ func (f *File) Read(b []byte) (n int, err error) { return } -func (f *File) ReadAt(b []byte, off int64) (n int, err error) { - return 0, ErrNotImplemented +// ReadAt reads up to len(b) bytes from the File at the given absolute offset. +// It returns the number of bytes read and any error encountered, possible io.EOF. +// At end of file, Read returns 0, io.EOF. +func (f *File) ReadAt(b []byte, offset int64) (n int, err error) { + n, err = f.handle.ReadAt(b, offset) + if err != nil && err != io.EOF { + err = &PathError{"readat", f.name, err} + } + return } // Write writes len(b) bytes to the File. It returns the number of bytes written diff --git a/src/os/file_other.go b/src/os/file_other.go index 37f9675d..e8657ec9 100644 --- a/src/os/file_other.go +++ b/src/os/file_other.go @@ -27,6 +27,10 @@ func (f stdioFileHandle) Read(b []byte) (n int, err error) { return 0, ErrUnsupported } +func (f stdioFileHandle) ReadAt(b []byte, off int64) (n int, err error) { + return 0, ErrNotImplemented +} + // Write writes len(b) bytes to the output. It returns the number of bytes // written or an error if this file is not stdout or stderr. func (f stdioFileHandle) Write(b []byte) (n int, err error) { diff --git a/src/os/file_unix.go b/src/os/file_unix.go index 64230fd3..b694c6b4 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -2,7 +2,10 @@ package os -import "syscall" +import ( + "io" + "syscall" +) type syscallFd = int @@ -22,3 +25,16 @@ func Pipe() (r *File, w *File, err error) { } return } + +// ReadAt reads up to len(b) bytes from the File starting at the given absolute offset. +// It returns the number of bytes read and any error encountered, possibly io.EOF. +// At end of file, Pread returns 0, io.EOF. +// TODO: move to file_anyos once ReadAt is implemented for windows +func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) { + n, err = syscall.Pread(syscallFd(f), b, offset) + err = handleSyscallError(err) + if n == 0 && err == nil { + err = io.EOF + } + return +} diff --git a/src/os/file_windows.go b/src/os/file_windows.go index cf970380..e3593707 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -22,3 +22,11 @@ func Pipe() (r *File, w *File, err error) { } return } + +// ReadAt reads up to len(b) bytes from the File starting at the given absolute offset. +// It returns the number of bytes read and any error encountered, possibly io.EOF. +// At end of file, Pread returns 0, io.EOF. +// TODO: move to file_anyos once ReadAt is implemented for windows +func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) { + return -1, ErrNotImplemented +} diff --git a/src/os/filesystem.go b/src/os/filesystem.go index b0719c6c..86e78621 100644 --- a/src/os/filesystem.go +++ b/src/os/filesystem.go @@ -46,6 +46,9 @@ type FileHandle interface { // Read reads up to len(b) bytes from the file. Read(b []byte) (n int, err error) + // ReadAt reads up to len(b) bytes from the file starting at the given absolute offset + ReadAt(b []byte, offset int64) (n int, err error) + // Write writes up to len(b) bytes to the file. Write(b []byte) (n int, err error) diff --git a/src/os/os_test.go b/src/os/os_test.go new file mode 100644 index 00000000..25c48778 --- /dev/null +++ b/src/os/os_test.go @@ -0,0 +1,48 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os_test + +import ( + "io" + . "os" + "runtime" + "testing" +) + +// localTmp returns a local temporary directory not on NFS. +func localTmp() string { + return TempDir() +} + +func newFile(testName string, t *testing.T) (f *File) { + // TODO: use CreateTemp when it lands + f, err := OpenFile(TempDir()+"/_Go_"+testName, O_RDWR|O_CREATE, 0644) + if err != nil { + t.Fatalf("TempFile %s: %s", testName, err) + } + return +} + +func TestReadAt(t *testing.T) { + if runtime.GOOS == "windows" { + t.Log("TODO: implement Pread for Windows") + return + } + f := newFile("TestReadAt", t) + defer Remove(f.Name()) + defer f.Close() + + const data = "hello, world\n" + io.WriteString(f, data) + + b := make([]byte, 5) + n, err := f.ReadAt(b, 7) + if err != nil || n != len(b) { + t.Fatalf("ReadAt 7: %d, %v", n, err) + } + if string(b) != "world" { + t.Fatalf("ReadAt 7: have %q want %q", string(b), "world") + } +} diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 77db38e2..b3cf1ce9 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -37,6 +37,15 @@ func Read(fd int, p []byte) (n int, err error) { return } +func Pread(fd int, p []byte, offset int64) (n int, err error) { + buf, count := splitSlice(p) + n = libc_pread(int32(fd), buf, uint(count), offset) + if n < 0 { + err = getErrno() + } + return +} + func Seek(fd int, offset int64, whence int) (off int64, err error) { return 0, ENOSYS // TODO } @@ -169,6 +178,10 @@ func libc_getenv(name *byte) *byte //export read func libc_read(fd int32, buf *byte, count uint) int +// ssize_t pread(int fd, void *buf, size_t count, off_t offset); +//export pread +func libc_pread(fd int32, buf *byte, count uint, offset int64) int + // int open(const char *pathname, int flags, mode_t mode); //export open func libc_open(pathname *byte, flags int32, mode uint32) int32