aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2024-08-30 09:57:02 +0100
committerGitHub <noreply@github.com>2024-08-30 09:57:02 +0100
commitb020b91df2aa86ea3a1500ce0ce459035d26c140 (patch)
tree751a689c2ab7686c9c2ab9ac384e1f2f11fea42a /core
parent291048cb3bdb7f1dc7d57383a1efd6fe77677768 (diff)
parentcca385209b7b303455ad17f832add12a0afcf16d (diff)
Merge pull request #4112 from Feoramund/fix-test-io-issues
Add `core:io` test suite
Diffstat (limited to 'core')
-rw-r--r--core/bytes/buffer.odin36
-rw-r--r--core/bytes/reader.odin11
-rw-r--r--core/c/libc/stdio.odin2
-rw-r--r--core/io/util.odin14
-rw-r--r--core/os/file_windows.odin30
-rw-r--r--core/os/os2/errors_linux.odin8
-rw-r--r--core/os/os2/errors_windows.odin3
-rw-r--r--core/os/os2/file.odin6
-rw-r--r--core/os/os2/file_linux.odin22
-rw-r--r--core/os/os2/file_posix.odin17
-rw-r--r--core/os/os2/file_windows.odin22
-rw-r--r--core/os/os_darwin.odin13
-rw-r--r--core/os/os_freebsd.odin46
-rw-r--r--core/os/os_haiku.odin139
-rw-r--r--core/os/os_linux.odin16
-rw-r--r--core/os/os_netbsd.odin53
-rw-r--r--core/os/os_openbsd.odin141
-rw-r--r--core/os/os_windows.odin4
-rw-r--r--core/os/stream.odin15
-rw-r--r--core/sys/freebsd/syscalls.odin87
-rw-r--r--core/sys/windows/winerror.odin1
21 files changed, 500 insertions, 186 deletions
diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin
index a7e9b1c64..f4d883353 100644
--- a/core/bytes/buffer.odin
+++ b/core/bytes/buffer.odin
@@ -144,6 +144,9 @@ buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) {
}
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int, loc := #caller_location) -> (n: int, err: io.Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
b.last_read = .Invalid
if offset < 0 {
err = .Invalid_Offset
@@ -246,10 +249,13 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
}
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
b.last_read = .Invalid
if uint(offset) >= len(b.buf) {
- err = .Invalid_Offset
+ err = .EOF
return
}
n = copy(p, b.buf[offset:])
@@ -310,6 +316,27 @@ buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
return nil
}
+buffer_seek :: proc(b: ^Buffer, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+ abs: i64
+ switch whence {
+ case .Start:
+ abs = offset
+ case .Current:
+ abs = i64(b.off) + offset
+ case .End:
+ abs = i64(len(b.buf)) + offset
+ case:
+ return 0, .Invalid_Whence
+ }
+
+ abs_int := int(abs)
+ if abs_int < 0 {
+ return 0, .Invalid_Offset
+ }
+ b.last_read = .Invalid
+ b.off = abs_int
+ return abs, nil
+}
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
i := index_byte(b.buf[b.off:], delim)
@@ -395,14 +422,17 @@ _buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
return io._i64_err(buffer_write(b, p))
case .Write_At:
return io._i64_err(buffer_write_at(b, p, int(offset)))
+ case .Seek:
+ n, err = buffer_seek(b, offset, whence)
+ return
case .Size:
- n = i64(buffer_capacity(b))
+ n = i64(buffer_length(b))
return
case .Destroy:
buffer_destroy(b)
return
case .Query:
- return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Size, .Destroy})
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Destroy, .Query})
}
return 0, .Empty
}
diff --git a/core/bytes/reader.odin b/core/bytes/reader.odin
index 4b18345ba..2e1c5ed42 100644
--- a/core/bytes/reader.odin
+++ b/core/bytes/reader.odin
@@ -9,10 +9,11 @@ Reader :: struct {
prev_rune: int, // previous reading index of rune or < 0
}
-reader_init :: proc(r: ^Reader, s: []byte) {
+reader_init :: proc(r: ^Reader, s: []byte) -> io.Stream {
r.s = s
r.i = 0
r.prev_rune = -1
+ return reader_to_stream(r)
}
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
@@ -33,6 +34,9 @@ reader_size :: proc(r: ^Reader) -> i64 {
}
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
if r.i >= i64(len(r.s)) {
return 0, .EOF
}
@@ -42,6 +46,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
return
}
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
if off < 0 {
return 0, .Invalid_Offset
}
@@ -97,7 +104,6 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
return nil
}
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
- r.prev_rune = -1
abs: i64
switch whence {
case .Start:
@@ -114,6 +120,7 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
return 0, .Invalid_Offset
}
r.i = abs
+ r.prev_rune = -1
return abs, nil
}
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin
index 4be00ff0b..019389b0d 100644
--- a/core/c/libc/stdio.odin
+++ b/core/c/libc/stdio.odin
@@ -368,7 +368,7 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
return 0, .Empty
case .Query:
- return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size })
+ return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query })
}
return
}
diff --git a/core/io/util.odin b/core/io/util.odin
index c24eb99c5..e65a69fb3 100644
--- a/core/io/util.odin
+++ b/core/io/util.odin
@@ -340,6 +340,9 @@ _limited_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte,
l := (^Limited_Reader)(stream_data)
#partial switch mode {
case .Read:
+ if len(p) == 0 {
+ return 0, nil
+ }
if l.n <= 0 {
return 0, .EOF
}
@@ -376,11 +379,12 @@ Section_Reader :: struct {
limit: i64,
}
-section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
+section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) -> Reader {
s.r = r
+ s.base = off
s.off = off
s.limit = off + n
- return
+ return section_reader_to_stream(s)
}
section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
out.data = s
@@ -393,6 +397,9 @@ _section_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte,
s := (^Section_Reader)(stream_data)
#partial switch mode {
case .Read:
+ if len(p) == 0 {
+ return 0, nil
+ }
if s.off >= s.limit {
return 0, .EOF
}
@@ -404,6 +411,9 @@ _section_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte,
s.off += i64(n)
return
case .Read_At:
+ if len(p) == 0 {
+ return 0, nil
+ }
p, off := p, offset
if off < 0 || off >= s.limit - s.base {
diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin
index 3f6f781aa..375da6aff 100644
--- a/core/os/file_windows.odin
+++ b/core/os/file_windows.odin
@@ -192,6 +192,8 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
case 0: w = win32.FILE_BEGIN
case 1: w = win32.FILE_CURRENT
case 2: w = win32.FILE_END
+ case:
+ return 0, .Invalid_Whence
}
hi := i32(offset>>32)
lo := i32(offset)
@@ -223,11 +225,13 @@ file_size :: proc(fd: Handle) -> (i64, Error) {
MAX_RW :: 1<<30
@(private)
-pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
+pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
+ curr_off := seek(fd, 0, 1) or_return
+ defer seek(fd, curr_off, 0)
+
buf := data
if len(buf) > MAX_RW {
buf = buf[:MAX_RW]
-
}
o := win32.OVERLAPPED{
@@ -247,11 +251,13 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
return int(done), e
}
@(private)
-pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
+pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
+ curr_off := seek(fd, 0, 1) or_return
+ defer seek(fd, curr_off, 0)
+
buf := data
if len(buf) > MAX_RW {
buf = buf[:MAX_RW]
-
}
o := win32.OVERLAPPED{
@@ -271,13 +277,6 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
/*
read_at returns n: 0, err: 0 on EOF
-on Windows, read_at changes the position of the file cursor, on *nix, it does not.
-
- bytes: [8]u8{}
- read_at(fd, bytes, 0)
- read(fd, bytes)
-
-will read from the location twice on *nix, and from two different locations on Windows
*/
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 {
@@ -302,15 +301,6 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
return
}
-/*
-on Windows, write_at changes the position of the file cursor, on *nix, it does not.
-
- bytes: [8]u8{}
- write_at(fd, bytes, 0)
- write(fd, bytes)
-
-will write to the location twice on *nix, and to two different locations on Windows
-*/
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 {
return 0, .Invalid_Offset
diff --git a/core/os/os2/errors_linux.odin b/core/os/os2/errors_linux.odin
index 29815bf79..ed55ea15e 100644
--- a/core/os/os2/errors_linux.odin
+++ b/core/os/os2/errors_linux.odin
@@ -154,6 +154,14 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error {
return .Exist
case .ENOENT:
return .Not_Exist
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EPIPE:
+ return .Broken_Pipe
+ case .EBADF:
+ return .Invalid_File
+ case .ENOMEM:
+ return .Out_Of_Memory
}
return Platform_Error(i32(errno))
diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin
index 6748c1167..8a9a47ca6 100644
--- a/core/os/os2/errors_windows.odin
+++ b/core/os/os2/errors_windows.odin
@@ -52,6 +52,9 @@ _get_platform_error :: proc() -> Error {
case win32.ERROR_INVALID_HANDLE:
return .Invalid_File
+ case win32.ERROR_NEGATIVE_SEEK:
+ return .Invalid_Offset
+
case
win32.ERROR_BAD_ARGUMENTS,
win32.ERROR_INVALID_PARAMETER,
diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin
index f1c5b1fad..eedf8570c 100644
--- a/core/os/os2/file.odin
+++ b/core/os/os2/file.odin
@@ -132,6 +132,12 @@ name :: proc(f: ^File) -> string {
return _name(f)
}
+/*
+ Close a file and its stream.
+
+ Any further use of the file or its stream should be considered to be in the
+ same class of bugs as a use-after-free.
+*/
close :: proc(f: ^File) -> Error {
if f != nil {
return io.close(f.stream)
diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin
index 64683cf1e..ad6ddbf17 100644
--- a/core/os/os2/file_linux.odin
+++ b/core/os/os2/file_linux.odin
@@ -170,11 +170,23 @@ _name :: proc(f: ^File) -> string {
}
_seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
+ // We have to handle this here, because Linux returns EINVAL for both
+ // invalid offsets and invalid whences.
+ switch whence {
+ case .Start, .Current, .End:
+ break
+ case:
+ return 0, .Invalid_Whence
+ }
n, errno := linux.lseek(f.fd, offset, linux.Seek_Whence(whence))
- if errno != .NONE {
+ #partial switch errno {
+ case .EINVAL:
+ return 0, .Invalid_Offset
+ case .NONE:
+ return n, nil
+ case:
return -1, _get_platform_error(errno)
}
- return n, nil
}
_read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
@@ -189,6 +201,9 @@ _read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
}
_read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
if offset < 0 {
return 0, .Invalid_Offset
}
@@ -214,6 +229,9 @@ _write :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
}
_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
if offset < 0 {
return 0, .Invalid_Offset
}
diff --git a/core/os/os2/file_posix.odin b/core/os/os2/file_posix.odin
index 96b7ffe4e..dae85d224 100644
--- a/core/os/os2/file_posix.odin
+++ b/core/os/os2/file_posix.odin
@@ -419,9 +419,22 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
#assert(int(posix.Whence.CUR) == int(io.Seek_From.Current))
#assert(int(posix.Whence.END) == int(io.Seek_From.End))
+ switch whence {
+ case .Start, .Current, .End:
+ break
+ case:
+ err = .Invalid_Whence
+ return
+ }
+
n = i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
if n < 0 {
- err = .Unknown
+ #partial switch posix.get_errno() {
+ case .EINVAL:
+ err = .Invalid_Offset
+ case:
+ err = .Unknown
+ }
}
return
@@ -446,7 +459,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
return
case .Query:
- return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
case:
return 0, .Empty
diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin
index 74067464b..382156420 100644
--- a/core/os/os2/file_windows.odin
+++ b/core/os/os2/file_windows.odin
@@ -248,6 +248,8 @@ _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, er
case .Start: w = win32.FILE_BEGIN
case .Current: w = win32.FILE_CURRENT
case .End: w = win32.FILE_END
+ case:
+ return 0, .Invalid_Whence
}
hi := i32(offset>>32)
lo := i32(offset)
@@ -264,6 +266,11 @@ _read :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
}
_read_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
+ length := len(p)
+ if length == 0 {
+ return
+ }
+
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
if len(b) == 0 {
return 0, nil
@@ -318,7 +325,6 @@ _read_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
single_read_length: win32.DWORD
total_read: int
- length := len(p)
sync.shared_guard(&f.rw_mutex) // multiple readers
@@ -337,6 +343,10 @@ _read_internal :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
if single_read_length > 0 && ok {
total_read += int(single_read_length)
+ } else if single_read_length == 0 && ok {
+ // ok and 0 bytes means EOF:
+ // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
+ err = .EOF
} else {
err = _get_platform_error()
}
@@ -352,7 +362,7 @@ _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error)
buf = buf[:MAX_RW]
}
- curr_offset := _seek(f, offset, .Current) or_return
+ curr_offset := _seek(f, 0, .Current) or_return
defer _seek(f, curr_offset, .Start)
o := win32.OVERLAPPED{
@@ -421,7 +431,7 @@ _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error)
buf = buf[:MAX_RW]
}
- curr_offset := _seek(f, offset, .Current) or_return
+ curr_offset := _seek(f, 0, .Current) or_return
defer _seek(f, curr_offset, .Start)
o := win32.OVERLAPPED{
@@ -466,13 +476,13 @@ _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
_sync :: proc(f: ^File) -> Error {
if f != nil && f.impl != nil {
- return _flush((^File_Impl)(f.impl))
+ return _flush_internal((^File_Impl)(f.impl))
}
return nil
}
_flush :: proc(f: ^File_Impl) -> Error {
- return _flush(f)
+ return _flush_internal(f)
}
_flush_internal :: proc(f: ^File_Impl) -> Error {
handle := _handle(&f.file)
@@ -813,7 +823,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
err = error_to_io_error(ferr)
return
case .Query:
- return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
}
return 0, .Empty
}
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index 1253e9ae6..2bb6c0c59 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -777,10 +777,21 @@ write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
assert(fd != -1)
+ switch whence {
+ case SEEK_SET, SEEK_CUR, SEEK_END:
+ break
+ case:
+ return 0, .Invalid_Whence
+ }
final_offset := i64(_unix_lseek(fd, int(offset), c.int(whence)))
if final_offset == -1 {
- return 0, get_last_error()
+ errno := get_last_error()
+ switch errno {
+ case .EINVAL:
+ return 0, .Invalid_Offset
+ }
+ return 0, errno
}
return final_offset, nil
}
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index 41c487b2b..c05a06129 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -6,6 +6,7 @@ foreign import libc "system:c"
import "base:runtime"
import "core:strings"
import "core:c"
+import "core:sys/freebsd"
Handle :: distinct i32
File_Time :: distinct u64
@@ -446,8 +447,7 @@ close :: proc(fd: Handle) -> Error {
}
flush :: proc(fd: Handle) -> Error {
- // do nothing
- return nil
+ return cast(_Platform_Error)freebsd.fsync(cast(freebsd.Fd)fd)
}
// If you read or write more than `INT_MAX` bytes, FreeBSD returns `EINVAL`.
@@ -481,29 +481,45 @@ write :: proc(fd: Handle, data: []byte) -> (int, Error) {
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = read(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_read := min(uint(len(data)), MAX_RW)
+
+ bytes_read, errno := freebsd.pread(cast(freebsd.Fd)fd, data[:to_read], cast(freebsd.off_t)offset)
+
+ return bytes_read, cast(_Platform_Error)errno
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = write(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_write := min(uint(len(data)), MAX_RW)
+
+ bytes_written, errno := freebsd.pwrite(cast(freebsd.Fd)fd, data[:to_write], cast(freebsd.off_t)offset)
+
+ return bytes_written, cast(_Platform_Error)errno
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
+ switch whence {
+ case SEEK_SET, SEEK_CUR, SEEK_END:
+ break
+ case:
+ return 0, .Invalid_Whence
+ }
res := _unix_seek(fd, offset, c.int(whence))
if res == -1 {
- return -1, get_last_error()
+ errno := get_last_error()
+ switch errno {
+ case .EINVAL:
+ return 0, .Invalid_Offset
+ case:
+ return 0, errno
+ }
}
return res, nil
}
diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin
index 2d87c07a6..0d2c334be 100644
--- a/core/os/os_haiku.odin
+++ b/core/os/os_haiku.odin
@@ -119,49 +119,52 @@ S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK
foreign libc {
- @(link_name="_errorp") __error :: proc() -> ^c.int ---
-
- @(link_name="fork") _unix_fork :: proc() -> pid_t ---
- @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
-
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u16) -> Handle ---
- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
- @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
- @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
- @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
- @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
- @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
- @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
- @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
- @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
- @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
-
- @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
- @(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
- @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
- @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
- @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
- @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
-
- @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
- @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
- @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
-
- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
-
- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
-
- @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
- @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
- @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
- @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
+ @(link_name="_errorp") __error :: proc() -> ^c.int ---
+
+ @(link_name="fork") _unix_fork :: proc() -> pid_t ---
+ @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
+
+ @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u16) -> Handle ---
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="pread") _unix_pread :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="pwrite") _unix_pwrite :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
+ @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
+ @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
+ @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
+ @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
+ @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
+ @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
+ @(link_name="fsync") _unix_fsync :: proc(fd: Handle) -> c.int ---
+
+ @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
+ @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+ @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
+ @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
+ @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
+ @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
+ @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+ @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+
+ @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
+
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
+ @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+ @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
+ @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
}
MAXNAMLEN :: haiku.NAME_MAX
@@ -216,7 +219,10 @@ close :: proc(fd: Handle) -> Error {
}
flush :: proc(fd: Handle) -> Error {
- // do nothing
+ result := _unix_fsync(fd)
+ if result == -1 {
+ return get_last_error()
+ }
return nil
}
@@ -250,29 +256,48 @@ write :: proc(fd: Handle, data: []byte) -> (int, Error) {
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = read(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_read := min(uint(len(data)), MAX_RW)
+
+ bytes_read := _unix_pread(fd, raw_data(data), to_read, offset)
+ if bytes_read < 0 {
+ return -1, get_last_error()
+ }
+ return bytes_read, nil
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = write(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_write := min(uint(len(data)), MAX_RW)
+
+ bytes_written := _unix_pwrite(fd, raw_data(data), to_write, offset)
+ if bytes_written < 0 {
+ return -1, get_last_error()
+ }
+ return bytes_written, nil
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
+ switch whence {
+ case SEEK_SET, SEEK_CUR, SEEK_END:
+ break
+ case:
+ return 0, .Invalid_Whence
+ }
res := _unix_seek(fd, offset, c.int(whence))
if res == -1 {
- return -1, get_last_error()
+ errno := get_last_error()
+ switch errno {
+ case .BAD_VALUE:
+ return 0, .Invalid_Offset
+ }
+ return 0, errno
}
return res, nil
}
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index 81ff5077a..9132edbfe 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -584,8 +584,7 @@ close :: proc(fd: Handle) -> Error {
}
flush :: proc(fd: Handle) -> Error {
- // do nothing
- return nil
+ return _get_errno(unix.sys_fsync(int(fd)))
}
// If you read or write more than `SSIZE_MAX` bytes, result is implementation defined (probably an error).
@@ -654,9 +653,20 @@ write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
+ switch whence {
+ case SEEK_SET, SEEK_CUR, SEEK_END:
+ break
+ case:
+ return 0, .Invalid_Whence
+ }
res := unix.sys_lseek(int(fd), offset, whence)
if res < 0 {
- return -1, _get_errno(int(res))
+ errno := _get_errno(int(res))
+ switch errno {
+ case .EINVAL:
+ return 0, .Invalid_Offset
+ }
+ return 0, errno
}
return i64(res), nil
}
diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin
index 82a8dc1eb..a56c0b784 100644
--- a/core/os/os_netbsd.odin
+++ b/core/os/os_netbsd.odin
@@ -426,7 +426,9 @@ foreign libc {
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u32) -> Handle ---
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="pread") _unix_pread :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="pwrite") _unix_pwrite :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
@(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
@@ -441,6 +443,7 @@ foreign libc {
@(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
@(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
@(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int ---
+ @(link_name="fsync") _unix_fsync :: proc(fd: Handle) -> c.int ---
@(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@@ -504,7 +507,10 @@ close :: proc(fd: Handle) -> Error {
}
flush :: proc(fd: Handle) -> Error {
- // do nothing
+ result := _unix_fsync(fd)
+ if result == -1 {
+ return get_last_error()
+ }
return nil
}
@@ -535,29 +541,48 @@ write :: proc(fd: Handle, data: []byte) -> (int, Error) {
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = read(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_read := min(uint(len(data)), MAX_RW)
+
+ bytes_read := _unix_pread(fd, raw_data(data), to_read, offset)
+ if bytes_read < 0 {
+ return -1, get_last_error()
+ }
+ return bytes_read, nil
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = write(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_write := min(uint(len(data)), MAX_RW)
+
+ bytes_written := _unix_pwrite(fd, raw_data(data), to_write, offset)
+ if bytes_written < 0 {
+ return -1, get_last_error()
+ }
+ return bytes_written, nil
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
+ switch whence {
+ case SEEK_SET, SEEK_CUR, SEEK_END:
+ break
+ case:
+ return 0, .Invalid_Whence
+ }
res := _unix_seek(fd, offset, c.int(whence))
if res == -1 {
- return -1, get_last_error()
+ errno := get_last_error()
+ switch errno {
+ case .EINVAL:
+ return 0, .Invalid_Offset
+ }
+ return 0, errno
}
return res, nil
}
diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin
index 44eac8564..aff78dc60 100644
--- a/core/os/os_openbsd.odin
+++ b/core/os/os_openbsd.odin
@@ -343,50 +343,53 @@ AT_REMOVEDIR :: 0x08
@(default_calling_convention="c")
foreign libc {
- @(link_name="__error") __error :: proc() -> ^c.int ---
-
- @(link_name="fork") _unix_fork :: proc() -> pid_t ---
- @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
-
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u32) -> Handle ---
- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
- @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
- @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
- @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
- @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
- @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
- @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
- @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
- @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
- @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
- @(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle ---
-
- @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
- @(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
- @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
- @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
- @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
- @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
-
- @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
- @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
- @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
-
- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
-
- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
-
- @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
- @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
- @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
- @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
+ @(link_name="__error") __error :: proc() -> ^c.int ---
+
+ @(link_name="fork") _unix_fork :: proc() -> pid_t ---
+ @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
+
+ @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u32) -> Handle ---
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="pread") _unix_pread :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="pwrite") _unix_pwrite :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
+ @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
+ @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
+ @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
+ @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
+ @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
+ @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
+ @(link_name="fsync") _unix_fsync :: proc(fd: Handle) -> c.int ---
+ @(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle ---
+
+ @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
+ @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+ @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
+ @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
+ @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
+ @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
+ @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+ @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
+
+ @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
+
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
+ @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+ @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
+ @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
}
@(require_results)
@@ -428,7 +431,10 @@ close :: proc(fd: Handle) -> Error {
}
flush :: proc(fd: Handle) -> Error {
- // do nothing
+ result := _unix_fsync(fd)
+ if result == -1 {
+ return get_last_error()
+ }
return nil
}
@@ -463,29 +469,48 @@ write :: proc(fd: Handle, data: []byte) -> (int, Error) {
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = read(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_read := min(uint(len(data)), MAX_RW)
+
+ bytes_read := _unix_pread(fd, raw_data(data), to_read, offset)
+ if bytes_read < 0 {
+ return -1, get_last_error()
+ }
+ return bytes_read, nil
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr := seek(fd, offset, SEEK_CUR) or_return
- n, err = write(fd, data)
- _, err1 := seek(fd, curr, SEEK_SET)
- if err1 != nil && err == nil {
- err = err1
+ if len(data) == 0 {
+ return 0, nil
}
- return
+
+ to_write := min(uint(len(data)), MAX_RW)
+
+ bytes_written := _unix_pwrite(fd, raw_data(data), to_write, offset)
+ if bytes_written < 0 {
+ return -1, get_last_error()
+ }
+ return bytes_written, nil
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
+ switch whence {
+ case SEEK_SET, SEEK_CUR, SEEK_END:
+ break
+ case:
+ return 0, .Invalid_Whence
+ }
res := _unix_seek(fd, offset, c.int(whence))
if res == -1 {
- return -1, get_last_error()
+ errno := get_last_error()
+ switch errno {
+ case .EINVAL:
+ return 0, .Invalid_Offset
+ }
+ return 0, errno
}
return res, nil
}
diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin
index 273fe5af0..6fb0631cd 100644
--- a/core/os/os_windows.odin
+++ b/core/os/os_windows.odin
@@ -43,6 +43,7 @@ ERROR_BUFFER_OVERFLOW :: _Platform_Error(111)
ERROR_INSUFFICIENT_BUFFER :: _Platform_Error(122)
ERROR_MOD_NOT_FOUND :: _Platform_Error(126)
ERROR_PROC_NOT_FOUND :: _Platform_Error(127)
+ERROR_NEGATIVE_SEEK :: _Platform_Error(131)
ERROR_DIR_NOT_EMPTY :: _Platform_Error(145)
ERROR_ALREADY_EXISTS :: _Platform_Error(183)
ERROR_ENVVAR_NOT_FOUND :: _Platform_Error(203)
@@ -91,6 +92,9 @@ get_last_error :: proc "contextless" () -> Error {
case win32.ERROR_INVALID_HANDLE:
return .Invalid_File
+ case win32.ERROR_NEGATIVE_SEEK:
+ return .Invalid_Offset
+
case
win32.ERROR_BAD_ARGUMENTS,
win32.ERROR_INVALID_PARAMETER,
diff --git a/core/os/stream.odin b/core/os/stream.odin
index 8acbee489..39edc9cd5 100644
--- a/core/os/stream.odin
+++ b/core/os/stream.odin
@@ -21,6 +21,9 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
case .Flush:
os_err = flush(fd)
case .Read:
+ if len(p) == 0 {
+ return 0, nil
+ }
n_int, os_err = read(fd, p)
n = i64(n_int)
if n == 0 && os_err == nil {
@@ -28,18 +31,27 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
}
case .Read_At:
+ if len(p) == 0 {
+ return 0, nil
+ }
n_int, os_err = read_at(fd, p, offset)
n = i64(n_int)
if n == 0 && os_err == nil {
err = .EOF
}
case .Write:
+ if len(p) == 0 {
+ return 0, nil
+ }
n_int, os_err = write(fd, p)
n = i64(n_int)
if n == 0 && os_err == nil {
err = .EOF
}
case .Write_At:
+ if len(p) == 0 {
+ return 0, nil
+ }
n_int, os_err = write_at(fd, p, offset)
n = i64(n_int)
if n == 0 && os_err == nil {
@@ -58,5 +70,8 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
if err == nil && os_err != nil {
err = error_to_io_error(os_err)
}
+ if err != nil {
+ n = 0
+ }
return
}
diff --git a/core/sys/freebsd/syscalls.odin b/core/sys/freebsd/syscalls.odin
index 4a79bd56c..8590df46e 100644
--- a/core/sys/freebsd/syscalls.odin
+++ b/core/sys/freebsd/syscalls.odin
@@ -14,12 +14,15 @@ import "core:c"
// FreeBSD 15 syscall numbers
// See: https://alfonsosiciliano.gitlab.io/posts/2023-08-28-freebsd-15-system-calls.html
+SYS_read : uintptr : 3
+SYS_write : uintptr : 4
SYS_open : uintptr : 5
SYS_close : uintptr : 6
SYS_getpid : uintptr : 20
SYS_recvfrom : uintptr : 29
SYS_accept : uintptr : 30
SYS_fcntl : uintptr : 92
+SYS_fsync : uintptr : 95
SYS_socket : uintptr : 97
SYS_connect : uintptr : 98
SYS_bind : uintptr : 104
@@ -29,12 +32,46 @@ SYS_shutdown : uintptr : 134
SYS_setsockopt : uintptr : 105
SYS_sysctl : uintptr : 202
SYS__umtx_op : uintptr : 454
+SYS_pread : uintptr : 475
+SYS_pwrite : uintptr : 476
SYS_accept4 : uintptr : 541
//
// Odin syscall wrappers
//
+// Read input.
+//
+// The read() function appeared in Version 1 AT&T UNIX.
+read :: proc "contextless" (fd: Fd, buf: []u8) -> (int, Errno) {
+ result, ok := intrinsics.syscall_bsd(SYS_read,
+ cast(uintptr)fd,
+ cast(uintptr)raw_data(buf),
+ cast(uintptr)len(buf))
+
+ if !ok {
+ return 0, cast(Errno)result
+ }
+
+ return cast(int)result, nil
+}
+
+// Write output.
+//
+// The write() function appeared in Version 1 AT&T UNIX.
+write :: proc "contextless" (fd: Fd, buf: []u8) -> (int, Errno) {
+ result, ok := intrinsics.syscall_bsd(SYS_pwrite,
+ cast(uintptr)fd,
+ cast(uintptr)raw_data(buf),
+ cast(uintptr)len(buf))
+
+ if !ok {
+ return 0, cast(Errno)result
+ }
+
+ return cast(int)result, nil
+}
+
// Open or create a file for reading, writing or executing.
//
// The open() function appeared in Version 1 AT&T UNIX.
@@ -164,6 +201,16 @@ accept_nil :: proc "contextless" (s: Fd) -> (Fd, Errno) {
accept :: proc { accept_T, accept_nil }
+// Synchronize changes to a file.
+//
+// The fsync() system call appeared in 4.2BSD.
+fsync :: proc "contextless" (fd: Fd) -> Errno {
+ result, _ := intrinsics.syscall_bsd(SYS_fsync,
+ cast(uintptr)fd)
+
+ return cast(Errno)result
+}
+
// File control.
//
// The fcntl() system call appeared in 4.2BSD.
@@ -469,6 +516,46 @@ _umtx_op :: proc "contextless" (obj: rawptr, op: Userland_Mutex_Operation, val:
return cast(Errno)result
}
+// Read input without modifying the file pointer.
+//
+// The pread() function appeared in AT&T System V Release 4 UNIX.
+pread :: proc "contextless" (fd: Fd, buf: []u8, offset: off_t) -> (int, Errno) {
+ result, ok := intrinsics.syscall_bsd(SYS_pread,
+ cast(uintptr)fd,
+ cast(uintptr)raw_data(buf),
+ cast(uintptr)len(buf),
+ cast(uintptr)offset)
+
+ if !ok {
+ return 0, cast(Errno)result
+ }
+
+ return cast(int)result, nil
+}
+
+// Write output without modifying the file pointer.
+//
+// The pwrite() function appeared in AT&T System V Release 4 UNIX.
+//
+// BUGS
+//
+// The pwrite() system call appends the file without changing the file
+// offset if O_APPEND is set, contrary to IEEE Std 1003.1-2008 (“POSIX.1”)
+// where pwrite() writes into offset regardless of whether O_APPEND is set.
+pwrite :: proc "contextless" (fd: Fd, buf: []u8, offset: off_t) -> (int, Errno) {
+ result, ok := intrinsics.syscall_bsd(SYS_pwrite,
+ cast(uintptr)fd,
+ cast(uintptr)raw_data(buf),
+ cast(uintptr)len(buf),
+ cast(uintptr)offset)
+
+ if !ok {
+ return 0, cast(Errno)result
+ }
+
+ return cast(int)result, nil
+}
+
// Accept a connection on a socket.
//
// The accept4() system call appeared in FreeBSD 10.0.
diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin
index 8882dad71..d3df3b815 100644
--- a/core/sys/windows/winerror.odin
+++ b/core/sys/windows/winerror.odin
@@ -213,6 +213,7 @@ ERROR_BROKEN_PIPE : DWORD : 109
ERROR_CALL_NOT_IMPLEMENTED : DWORD : 120
ERROR_INSUFFICIENT_BUFFER : DWORD : 122
ERROR_INVALID_NAME : DWORD : 123
+ERROR_NEGATIVE_SEEK : DWORD : 131
ERROR_BAD_ARGUMENTS : DWORD : 160
ERROR_LOCK_FAILED : DWORD : 167
ERROR_ALREADY_EXISTS : DWORD : 183