fs: extracts syscallfs.ReaderAtOffset for WASI and gojs (#1037)
This extracts a utility `syscallfs.ReaderAtOffset()` to allow WASI and gojs to re-use the same logic to implement `syscall.Pread`. What's different than before is that if WASI passes multiple iovecs an emulated `ReaderAt` will seek to the read position on each call to `Read` vs once per loop. This was a design decision to keep the call sites compatible between files that implement ReaderAt and those that emulate them with Seeker (e.g. avoid the need for a read-scoped closer/ defer function). The main use case for emulation is `embed.file`, whose seek function is cheap, so there's little performance impact to this. Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -274,17 +274,13 @@ func syscallRead(mod api.Module, fd uint32, offset interface{}, p []byte) (n uin
|
||||
err = syscall.EBADF
|
||||
}
|
||||
|
||||
var reader io.Reader = f.File
|
||||
|
||||
if offset != nil {
|
||||
if s, ok := f.File.(io.Seeker); ok {
|
||||
if _, err := s.Seek(toInt64(offset), io.SeekStart); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
return 0, syscall.ENOTSUP
|
||||
}
|
||||
reader = syscallfs.ReaderAtOffset(f.File, toInt64(offset))
|
||||
}
|
||||
|
||||
if nRead, e := f.File.Read(p); e == nil || e == io.EOF {
|
||||
if nRead, e := reader.Read(p); e == nil || e == io.EOF {
|
||||
// fs_js.go cannot parse io.EOF so coerce it to nil.
|
||||
// See https://github.com/golang/go/issues/43913
|
||||
n = uint32(nRead)
|
||||
@@ -309,7 +305,7 @@ func (jsfsWrite) invoke(ctx context.Context, mod api.Module, args ...interface{}
|
||||
}
|
||||
offset := goos.ValueToUint32(args[2])
|
||||
byteCount := goos.ValueToUint32(args[3])
|
||||
fOffset := args[4] // nil unless Pread
|
||||
fOffset := args[4] // nil unless Pwrite
|
||||
callback := args[5].(funcWrapper)
|
||||
|
||||
if byteCount > 0 { // empty is possible on EOF
|
||||
|
||||
25
internal/gojs/testdata/fs/main.go
vendored
25
internal/gojs/testdata/fs/main.go
vendored
@@ -1,7 +1,9 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
@@ -34,12 +36,35 @@ func testAdHoc() {
|
||||
}
|
||||
}
|
||||
|
||||
// Read the full contents of the file using io.Reader
|
||||
b, err := os.ReadFile("/animals.txt")
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
fmt.Println("contents:", string(b))
|
||||
|
||||
// Re-open the same file to test io.ReaderAt
|
||||
f, err := os.Open("/animals.txt")
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Seek to an arbitrary position.
|
||||
if _, err = f.Seek(4, io.SeekStart); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
b1 := make([]byte, len(b))
|
||||
// We expect to revert to the original position on ReadAt zero.
|
||||
if _, err = f.ReadAt(b1, 0); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(b, b1) {
|
||||
log.Panicln("unexpected ReadAt contents: ", string(b1))
|
||||
}
|
||||
|
||||
b, err = os.ReadFile("/empty.txt")
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
|
||||
Reference in New Issue
Block a user