-
Notifications
You must be signed in to change notification settings - Fork 47
Implement missing os.File methods #1553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
2f1bf4a
44cde94
efd2774
d371a29
c83e1a2
d78a47e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "os" | ||
| ) | ||
|
|
||
| func main() { | ||
| // Test file operations | ||
| testFile := "test_file.txt" | ||
|
|
||
| // Clean up at the end | ||
| defer os.Remove(testFile) | ||
|
|
||
| // Test Write and WriteString | ||
| f, err := os.Create(testFile) | ||
| if err != nil { | ||
| panic("Create failed: " + err.Error()) | ||
| } | ||
|
|
||
| // Test Write | ||
| data := []byte("Hello, World!\n") | ||
| n, err := f.Write(data) | ||
| if err != nil || n != len(data) { | ||
| panic("Write failed") | ||
| } | ||
|
|
||
| // Test WriteString | ||
| n, err = f.WriteString("Test WriteString\n") | ||
| if err != nil || n != 17 { | ||
| panic("WriteString failed") | ||
| } | ||
|
|
||
| f.Close() | ||
|
|
||
| // Test ReadAt | ||
| f, err = os.Open(testFile) | ||
| if err != nil { | ||
| panic("Open failed: " + err.Error()) | ||
| } | ||
|
|
||
| buf := make([]byte, 5) | ||
| n, err = f.ReadAt(buf, 0) | ||
| if err != nil || n != 5 || string(buf) != "Hello" { | ||
| panic("ReadAt failed: expected 'Hello'") | ||
| } | ||
|
|
||
| n, err = f.ReadAt(buf, 7) | ||
| if err != nil || n != 5 || string(buf) != "World" { | ||
| panic("ReadAt failed: expected 'World'") | ||
| } | ||
|
|
||
| f.Close() | ||
|
|
||
| // Test WriteAt with offset 0 | ||
| f, err = os.OpenFile(testFile, os.O_RDWR, 0644) | ||
| if err != nil { | ||
| panic("OpenFile failed: " + err.Error()) | ||
| } | ||
|
|
||
| n, err = f.WriteAt([]byte("XXXXX"), 0) | ||
| if err != nil || n != 5 { | ||
| panic("WriteAt at offset 0 failed") | ||
| } | ||
|
|
||
| // Test WriteAt with non-zero offset | ||
| n, err = f.WriteAt([]byte("YYYYY"), 7) | ||
| if err != nil || n != 5 { | ||
| panic("WriteAt at offset 7 failed") | ||
| } | ||
|
|
||
| f.Close() | ||
|
|
||
| // Verify WriteAt results | ||
| f, err = os.Open(testFile) | ||
| if err != nil { | ||
| panic("Open failed: " + err.Error()) | ||
| } | ||
|
|
||
| buf = make([]byte, 5) | ||
| n, err = f.ReadAt(buf, 0) | ||
| if err != nil || n != 5 || string(buf) != "XXXXX" { | ||
| panic("WriteAt verification at offset 0 failed: expected 'XXXXX'") | ||
| } | ||
|
|
||
| buf = make([]byte, 5) | ||
| n, err = f.ReadAt(buf, 7) | ||
| if err != nil || n != 5 || string(buf) != "YYYYY" { | ||
| panic("WriteAt verification at offset 7 failed: expected 'YYYYY'") | ||
| } | ||
|
|
||
| f.Close() | ||
|
|
||
| // Test Seek | ||
| f, err = os.Open(testFile) | ||
| if err != nil { | ||
| panic("Open failed: " + err.Error()) | ||
| } | ||
|
|
||
| // Seek to position 7 | ||
| pos, err := f.Seek(7, 0) // SEEK_SET = 0 | ||
| if err != nil || pos != 7 { | ||
| panic("Seek failed") | ||
| } | ||
|
|
||
| buf = make([]byte, 5) | ||
| n, err = f.Read(buf) | ||
| if err != nil || n != 5 || string(buf) != "World" { | ||
|
||
| panic("Seek test failed: expected 'World'") | ||
| } | ||
|
|
||
| // Seek from current position | ||
| pos, err = f.Seek(2, 1) // SEEK_CUR = 1 | ||
| if err != nil || pos != 14 { | ||
| panic("Seek from current failed") | ||
| } | ||
|
|
||
| // Seek from end | ||
| pos, err = f.Seek(-5, 2) // SEEK_END = 2 | ||
| if err != nil { | ||
| panic("Seek from end failed") | ||
| } | ||
|
|
||
| f.Close() | ||
|
|
||
| println("All os.File tests passed!") | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,8 +7,13 @@ | |
| package os | ||
|
|
||
| import ( | ||
| "io" | ||
| "syscall" | ||
| "time" | ||
| "unsafe" | ||
|
|
||
| c "github.com/goplus/llgo/runtime/internal/clite" | ||
| "github.com/goplus/llgo/runtime/internal/clite/os" | ||
| ) | ||
|
|
||
| // Close closes the File, rendering it unusable for I/O. | ||
|
|
@@ -26,23 +31,27 @@ func (f *File) Close() error { | |
| // It returns the number of bytes read and the error, if any. | ||
| // EOF is signaled by a zero count with err set to nil. | ||
| func (f *File) pread(b []byte, off int64) (n int, err error) { | ||
| /* | ||
| n, err = f.pfd.Pread(b, off) | ||
| runtime.KeepAlive(f) | ||
| return n, err | ||
| */ | ||
| panic("todo: os.(*File).pread") | ||
| ret := os.Pread(c.Int(f.fd), unsafe.Pointer(unsafe.SliceData(b)), uintptr(len(b)), os.OffT(off)) | ||
| if ret > 0 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @xgopilot if ret >= 0 { ... }
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ Issue Already FixedThe return value handling has been corrected in commit What was changed:
Why this matters:
The code now correctly handles all three possible return value ranges from the C |
||
| return int(ret), nil | ||
| } | ||
| if ret == 0 { | ||
| return 0, nil | ||
| } | ||
| return 0, syscall.Errno(os.Errno()) | ||
| } | ||
|
|
||
| // pwrite writes len(b) bytes to the File starting at byte offset off. | ||
| // It returns the number of bytes written and an error, if any. | ||
| func (f *File) pwrite(b []byte, off int64) (n int, err error) { | ||
| /* | ||
| n, err = f.pfd.Pwrite(b, off) | ||
| runtime.KeepAlive(f) | ||
| return n, err | ||
| */ | ||
| panic("todo: os.(*File).pwrite") | ||
| ret := os.Pwrite(c.Int(f.fd), unsafe.Pointer(unsafe.SliceData(b)), uintptr(len(b)), os.OffT(off)) | ||
| if ret > 0 { | ||
| return int(ret), nil | ||
| } | ||
| if ret == 0 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @xgopilot 如果 ret 为 0, 但 len(b) 不为 0 的情况,需要检查是否处于阻塞模式,以判断处理。
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ Changes ImplementedI've addressed your concern about handling the case when Implementation DetailsUpdated both For if ret == 0 {
// When pwrite returns 0 but we have data to write, check if the file
// is in non-blocking mode and would block.
if len(b) > 0 {
// Check if there's an errno set (e.g., EAGAIN for non-blocking mode)
errno := os.Errno()
if errno == os.EAGAIN {
// Non-blocking mode: operation would block
return 0, syscall.Errno(errno)
}
// Blocking mode or no specific error: this is unexpected
// Return error to prevent infinite loops in WriteAt
return 0, io.ErrUnexpectedEOF
}
return 0, nil
}For if ret == 0 {
// When pread returns 0, it typically indicates EOF.
// However, for non-blocking files, check if EAGAIN is set.
if len(b) > 0 {
errno := os.Errno()
if errno == os.EAGAIN {
// Non-blocking mode: operation would block
return 0, syscall.Errno(errno)
}
}
// For blocking mode or regular EOF, return 0 with nil error
return 0, nil
}What This Fixes
Verification
The implementation now correctly distinguishes between blocking and non-blocking file descriptors when handling the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ Task CompletedEnhanced both Changes MadeFor pwrite (file_posix.go:60-81):
For pread (file_posix.go:33-53):
Why This MattersThis implementation addresses @visualfc's concern about checking blocking mode when
Verification✅ Code formatting: The fix has been applied and is ready for review! |
||
| return 0, nil | ||
| } | ||
| return 0, syscall.Errno(os.Errno()) | ||
| } | ||
|
|
||
| // syscallMode returns the syscall-specific mode bits from Go's portable mode bits. | ||
|
|
@@ -223,6 +232,23 @@ func (f *File) checkValid(op string) error { | |
| return nil | ||
| } | ||
|
|
||
| // seek sets the file offset for the next Read or Write on file to offset. | ||
| func (f *File) seek(offset int64, whence int) (int64, error) { | ||
| ret := os.Lseek(c.Int(f.fd), os.OffT(offset), c.Int(whence)) | ||
| if ret < 0 { | ||
| return 0, syscall.Errno(os.Errno()) | ||
| } | ||
| return int64(ret), nil | ||
| } | ||
|
|
||
| // readFrom is a platform-specific implementation of io.ReaderFrom. | ||
| // It returns handled=false to fall back to the generic implementation. | ||
| func (f *File) readFrom(r io.Reader) (n int64, handled bool, err error) { | ||
| // For now, we don't have platform-specific optimizations | ||
| // Let the generic implementation handle it | ||
| return 0, false, nil | ||
| } | ||
|
|
||
| // ignoringEINTR makes a function call and repeats it if it returns an | ||
| // EINTR error. This appears to be required even though we install all | ||
| // signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xgopilot WriteAt add test offset != 0
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WriteAt test with offset != 0 added
Added a comprehensive test for
WriteAtwith non-zero offset (offset 7) to complement the existing test at offset 0.Changes made:
WriteAttest at offset 7 that writes "YYYYY"The test now verifies that:
WriteAtworks correctly at offset 0 (writes "XXXXX")WriteAtworks correctly at offset 7 (writes "YYYYY")Changes have been committed and pushed to the PR branch.