Browse Source

Fix unhandled EOF in streaming io on Windows

Jeroen van Rijn 1 year ago
parent
commit
0c97f6aa4e
2 changed files with 21 additions and 20 deletions
  1. 16 16
      core/os/file_windows.odin
  2. 5 4
      core/os/stream.odin

+ 16 - 16
core/os/file_windows.odin

@@ -149,7 +149,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
 	return
 	return
 }
 }
 
 
-read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
 	if len(data) == 0 {
 	if len(data) == 0 {
 		return 0, ERROR_NONE
 		return 0, ERROR_NONE
 	}
 	}
@@ -158,32 +158,32 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	
 	
 	m: u32
 	m: u32
 	is_console := win32.GetConsoleMode(handle, &m)
 	is_console := win32.GetConsoleMode(handle, &m)
-
-	single_read_length: win32.DWORD
-	total_read: int
 	length := len(data)
 	length := len(data)
 
 
 	// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
 	// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
 	to_read := min(i64(length), MAX_RW)
 	to_read := min(i64(length), MAX_RW)
 
 
-	e: win32.BOOL
 	if is_console {
 	if is_console {
-		n, err := read_console(handle, data[total_read:][:to_read])
-		total_read += n
+		total_read, err = read_console(handle, data[total_read:][:to_read])
 		if err != 0 {
 		if err != 0 {
-			return int(total_read), err
+			return total_read, err
 		}
 		}
 	} else {
 	} else {
 		// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
 		// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
-		e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
-	}
-	if single_read_length <= 0 || !e {
-		err := Errno(win32.GetLastError())
-		return int(total_read), err
+		bytes_read: win32.DWORD
+		if e := win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &bytes_read, nil); e {
+			// Successful read can mean two things, including EOF, see:
+			// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
+			if bytes_read == 0 {
+				return 0, ERROR_HANDLE_EOF
+			} else {
+				return int(bytes_read), ERROR_NONE
+			}
+		} else {
+			return 0, Errno(win32.GetLastError())
+		}
 	}
 	}
-	total_read += int(single_read_length)
-	
-	return int(total_read), ERROR_NONE
+	return total_read, ERROR_NONE
 }
 }
 
 
 seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
 seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {

+ 5 - 4
core/os/stream.odin

@@ -27,9 +27,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
 	case .Read:
 	case .Read:
 		n_int, os_err = read(fd, p)
 		n_int, os_err = read(fd, p)
 		n = i64(n_int)
 		n = i64(n_int)
-		if os_err != 0 {
-			err = .Unknown
-		}
+
 	case .Read_At:
 	case .Read_At:
 		when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
 		when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
 			n_int, os_err = read_at(fd, p, offset)
 			n_int, os_err = read_at(fd, p, offset)
@@ -57,7 +55,10 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
 		}
 		}
 	}
 	}
 	if err == nil && os_err != 0 {
 	if err == nil && os_err != 0 {
-		err = .Unknown
+		switch os_err {
+		case ERROR_HANDLE_EOF: err = .EOF
+		case:                  err = .Unknown
+		}
 	}
 	}
 	return
 	return
 }
 }