Files
Viper_VRTE/w32/fileio.vp
T
TermiNexus e71daeea0e docs(includes): R11 注释英文化——75 个文件的中文注释改写为英文短注释
按规范「注释只讲解作用,不掺杂历史原因,尽量英文」:把 includes 下 75 个 .vp 的中文 # 注释与三引号 docstring 改写为英文,每个注释块压到 2 行以内。

保留要点:作用、约束、危险警告(不得/必须/注意)、ABI 与类型约定(如 BYTEPTR | t.CPtr = i8** 的跨模块坍缩坑)、位宽/长度约定、fail-fast 要求、内存与句柄所有权。

未动:文件头注释(规范豁免,仍是中文)、运行时中文字符串常量(printf / fatal_error / MlirFail 等用户可见消息,属代码字面量)。

验证:独立脚本比对 HEAD 与工作区的代码骨架(剥离 # 注释与三引号 docstring 后的非空行)——75 个文件逐字节一致;行尾 CRLF/LF 未被改动;全文剩余中文仅 547 行头注释、0 条行尾注释。bootstrap 三重重编 SUCCESS;全量 77/77 顺序回归通过。
2026-10-05 16:18:44 +08:00

457 lines
17 KiB
Plaintext

import t, c
from stdint import *
import stdio
import stdlib
import string
import w32.win32base
import w32.win32file
enum MODE:
R = 0
W = 1
A = 2
RP = 3
WP = 4
AP = 5
X = 6
XP = 7
enum FRESULT:
OK = 0
ERR = -1
ERR_CLOSED = -2
ERR_PERM = -3
ERR_IO = -4
ERR_NOTFOUND = -5
define SEEK_SET = 0
define SEEK_CUR = 1
define SEEK_END = 2
define INVALID_SET_FILE_POINTER = -1
define SHARE_READ = 0x00000001
define SHARE_WRITE = 0x00000002
define SHARE_DELETE = 0x00000004
# =============================================================================
# Environment variable expansion (the only implementation in the path layer).
#
# Expands "%VAR%" in paths before opening a file (Win32 only called when the path contains '%'); returns a stdlib.malloc'd string (caller frees) or None on failure / no expansion.
# UNC, relative, drive, ".." and long paths are passed through unchanged; A-series results share the ANSI code page of existing A-series call sites.
# =============================================================================
extern def ExpandEnvironmentStringsA(lpSrc: LPCSTR, lpDst: CHARPTR, nSize: ULONG) -> ULONG
# Expansion buffer cap (incl. NUL); exceeding it is treated as failure and falls back to the original path
define EXPAND_ENV_CAP = 4096
def ExpandEnvironmentPath(path: str) -> str:
"""Expand env vars when path contains '%': returns a new string (caller stdlib.free) or None if not needed / on failure"""
if path is None:
return None
# Fast path: no '%' -> return None (no Win32 call, path unchanged)
if string.strchr(path, '%') is None:
return None
buf: str = stdlib.malloc(EXPAND_ENV_CAP)
if buf is None:
return None
n: ULONG = ExpandEnvironmentStringsA(path, buf, ULONG(EXPAND_ENV_CAP))
# n == 0: failure; n > cap: required TCHAR count (incl. NUL) exceeds buffer, content unusable
if n == 0 or int(n) > EXPAND_ENV_CAP:
stdlib.free(buf)
return None
return buf
def create_file_utf8(path: str, access: ULONG, share: ULONG, disposition: ULONG,
flags: ULONG) -> w32.win32base.HANDLE:
"""Open/create a file with path-encoding fallback: '%' paths are expanded first (original on failure); valid UTF-8 -> UTF-16 CreateFileW, invalid UTF-8 (ANSI argv) -> CreateFileA. See win32base."""
# Expanded string must live past the CreateFile call (kernel holds the handle; the string is discarded after)
ep: str = ExpandEnvironmentPath(path)
use_path: str = path
if ep is not None:
use_path = ep
h: w32.win32base.HANDLE = None
if w32.win32base.utf8_is_valid(use_path) == 0:
h = w32.win32file.CreateFileA(use_path, access, share, None, disposition,
flags, None)
else:
wp: w32.win32base.WCHARPTR = w32.win32base.utf8_to_utf16_dup(use_path)
if wp is None:
# Wide-char buffer alloc failed: fall back to A path; non-ASCII paths fail naturally and return
# INVALID_HANDLE_VALUE (error not masked, just the same failure exit).
h = w32.win32file.CreateFileA(use_path, access, share, None, disposition,
flags, None)
else:
h = w32.win32file.CreateFileW(
t.CPtr(wp), access, share, None, disposition, flags, None)
stdlib.free(wp)
if ep is not None:
stdlib.free(ep)
return h
class File:
handle: w32.win32base.HANDLE
closed: bool
can_read: bool
can_write: bool
is_append: bool
_share_mode: ULONG
def __init__(self, filename: str, mode: ULONG, share: ULONG = SHARE_READ):
self.closed = True
self.can_read = False
self.can_write = False
self.is_append = False
self._share_mode = share
access: ULONG = 0
disposition: ULONG = w32.win32file.OPEN_EXISTING
if mode == MODE.R:
access = w32.win32file.GENERIC_READ
disposition = w32.win32file.OPEN_EXISTING
self.can_read = True
elif mode == MODE.W:
access = w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_ALWAYS
self.can_write = True
elif mode == MODE.A:
access = w32.win32file.GENERIC_WRITE
disposition = w32.win32file.OPEN_ALWAYS
self.can_write = True
self.is_append = True
elif mode == MODE.RP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.OPEN_EXISTING
self.can_read = True
self.can_write = True
elif mode == MODE.WP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_ALWAYS
self.can_read = True
self.can_write = True
elif mode == MODE.AP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.OPEN_ALWAYS
self.can_read = True
self.can_write = True
self.is_append = True
elif mode == MODE.X:
access = w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_NEW
self.can_write = True
elif mode == MODE.XP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_NEW
self.can_read = True
self.can_write = True
else:
access = w32.win32file.GENERIC_READ
disposition = w32.win32file.OPEN_EXISTING
self.can_read = True
self.handle = create_file_utf8(
filename, access, self._share_mode, disposition,
w32.win32file.FILE_ATTRIBUTE_NORMAL
)
if self.handle == w32.win32base.INVALID_HANDLE_VALUE:
self.closed = True
return
self.closed = False
if self.is_append:
self.seek(0, SEEK_END)
def __enter__(self) -> 'File' | t.CPtr:
return self
def __exit__(self):
self.close()
def read(self, buf: bytes, count: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_read: return FRESULT.ERR_PERM
bytes_read: ULONG = 0
result: BOOL = w32.win32file.ReadFile(self.handle, buf, count, t.CPtr(&bytes_read), None)
if result == 0: return FRESULT.ERR_IO
return LONG(bytes_read)
def write(self, buf: bytes, count: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_write: return FRESULT.ERR_PERM
bytes_written: ULONG = 0
result: BOOL = w32.win32file.WriteFile(self.handle, buf, count, &bytes_written, None)
if result == 0: return FRESULT.ERR_IO
return LONG(bytes_written)
def write_str(self, s: str) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_write: return FRESULT.ERR_PERM
length: ULONG = 0
p: bytes = s
while *p != 0:
length += 1
p += 1
return self.write(s, length)
def seek(self, offset: LONG, origin: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
result: LONG = w32.win32file.SetFilePointer(self.handle, offset, None, origin)
if result == -1: return FRESULT.ERR_IO
return result
def tell(self) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
return self.seek(0, SEEK_CUR)
def close(self) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
w32.win32base.CloseHandle(self.handle)
self.closed = True
return FRESULT.OK
def flush(self) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
w32.win32file.FlushFileBuffers(self.handle)
return FRESULT.OK
def size(self) -> LONGLONG:
if self.closed: return -1
li: w32.win32base.LARGE_INTEGER = w32.win32base.LARGE_INTEGER()
result: BOOL = w32.win32file.GetFileSizeEx(self.handle, t.CPtr(&li))
if result == 0: return -1
return li.QuadPart
def readline(self, buf: bytes, max_count: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_read: return FRESULT.ERR_PERM
if max_count < 2: return FRESULT.ERR
total: ULONG = 0
bytes_read: ULONG = 0
ch: int = 0
while total < max_count - 1:
bytes_read = 0
result: BOOL = w32.win32file.ReadFile(self.handle, buf + total, 1, t.CPtr(&bytes_read), None)
if result == 0 or bytes_read == 0: break
# Explicit byte read: indexing loads 1 byte by the pointer element type (i8), same as Repl.vp _read_line.
# Do not use c.Deref(buf + total): its width depends on whether the pointer is i8*, but 1 byte is required here.
ch = buf[total]
total += 1
if ch == 10: break
if total < max_count:
buf[total] = 0
return LONG(total)
def read_all(self, buf: bytes, buf_size: ULONG) -> LONG:
"""Read the whole file into buf as a C string (seeks to 0 first); returns bytes read, negative on error.
buf_size is capacity incl. terminator: reads at most buf_size-1 bytes then writes a NUL at buf[ret]. A file longer than buf_size-1 is silently truncated; compare f.size() first to detect truncation."""
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_read: return FRESULT.ERR_PERM
if buf_size < 2: return FRESULT.ERR
file_size: LONGLONG = self.size()
if file_size < 0: return FRESULT.ERR_IO
to_read: ULONG = 0
if file_size < buf_size - 1:
to_read = ULONG(file_size)
else:
to_read = buf_size - 1
self.seek(0, SEEK_SET)
bytes_read: ULONG = 0
result: BOOL = w32.win32file.ReadFile(self.handle, buf, to_read, t.CPtr(&bytes_read), None)
if result == 0: return FRESULT.ERR_IO
if bytes_read < buf_size:
buf[bytes_read] = 0
return LONG(bytes_read)
class FileW:
handle: w32.win32base.HANDLE
closed: bool
can_read: bool
can_write: bool
is_append: bool
_share_mode: ULONG
def __init__(self, filename: w32.win32base.LPCWSTR, mode: ULONG, share: ULONG = SHARE_READ):
self.closed = True
self.can_read = False
self.can_write = False
self.is_append = False
self._share_mode = share
access: ULONG = 0
disposition: ULONG = w32.win32file.OPEN_EXISTING
if mode == MODE.R:
access = w32.win32file.GENERIC_READ
disposition = w32.win32file.OPEN_EXISTING
self.can_read = True
elif mode == MODE.W:
access = w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_ALWAYS
self.can_write = True
elif mode == MODE.A:
access = w32.win32file.GENERIC_WRITE
disposition = w32.win32file.OPEN_ALWAYS
self.can_write = True
self.is_append = True
elif mode == MODE.RP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.OPEN_EXISTING
self.can_read = True
self.can_write = True
elif mode == MODE.WP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_ALWAYS
self.can_read = True
self.can_write = True
elif mode == MODE.AP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.OPEN_ALWAYS
self.can_read = True
self.can_write = True
self.is_append = True
elif mode == MODE.X:
access = w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_NEW
self.can_write = True
elif mode == MODE.XP:
access = w32.win32file.GENERIC_READ | w32.win32file.GENERIC_WRITE
disposition = w32.win32file.CREATE_NEW
self.can_read = True
self.can_write = True
else:
access = w32.win32file.GENERIC_READ
disposition = w32.win32file.OPEN_EXISTING
self.can_read = True
self.handle = w32.win32file.CreateFileW(
filename, access, self._share_mode,
None, disposition, w32.win32file.FILE_ATTRIBUTE_NORMAL, None
)
if self.handle == w32.win32base.INVALID_HANDLE_VALUE:
self.closed = True
return
self.closed = False
if self.is_append:
self.seek(0, SEEK_END)
def __enter__(self) -> 'FileW' | t.CPtr:
return self
def __exit__(self):
self.close()
def read(self, buf: bytes, count: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_read: return FRESULT.ERR_PERM
bytes_read: ULONG = 0
result: BOOL = w32.win32file.ReadFile(self.handle, buf, count, t.CPtr(&bytes_read), None)
if result == 0: return FRESULT.ERR_IO
return LONG(bytes_read)
def write(self, buf: bytes, count: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_write: return FRESULT.ERR_PERM
bytes_written: ULONG = 0
result: BOOL = w32.win32file.WriteFile(self.handle, buf, count, t.CPtr(&bytes_written), None)
if result == 0: return FRESULT.ERR_IO
return LONG(bytes_written)
def write_str(self, s: str) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_write: return FRESULT.ERR_PERM
length: ULONG = 0
p: bytes = s
while *p != 0:
length += 1
p += 1
return self.write(s, length)
def seek(self, offset: LONG, origin: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
result: LONG = w32.win32file.SetFilePointer(self.handle, offset, None, origin)
if result == -1: return FRESULT.ERR_IO
return result
def tell(self) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
return self.seek(0, SEEK_CUR)
def close(self) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
w32.win32base.CloseHandle(self.handle)
self.closed = True
return FRESULT.OK
def flush(self) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
w32.win32file.FlushFileBuffers(self.handle)
return FRESULT.OK
def size(self) -> LONGLONG:
if self.closed: return -1
li: w32.win32base.LARGE_INTEGER = w32.win32base.LARGE_INTEGER()
result: BOOL = w32.win32file.GetFileSizeEx(self.handle, t.CPtr(&li))
if result == 0: return -1
return li.QuadPart
def readline(self, buf: bytes, max_count: ULONG) -> LONG:
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_read: return FRESULT.ERR_PERM
if max_count < 2: return FRESULT.ERR
total: ULONG = 0
bytes_read: ULONG = 0
ch: int = 0
while total < max_count - 1:
bytes_read = 0
result: BOOL = w32.win32file.ReadFile(self.handle, buf + total, 1, t.CPtr(&bytes_read), None)
if result == 0 or bytes_read == 0: break
# Explicit byte read: indexing loads 1 byte by the pointer element type (i8), same as Repl.vp _read_line.
# Do not use c.Deref(buf + total): its width depends on whether the pointer is i8*, but 1 byte is required here.
ch = buf[total]
total += 1
if ch == 10: break
if total < max_count:
buf[total] = 0
return LONG(total)
def read_all(self, buf: bytes, buf_size: ULONG) -> LONG:
"""Read the whole file into buf as a C string (seeks to 0 first); returns bytes read, negative on error.
buf_size is capacity incl. terminator: reads at most buf_size-1 bytes then writes a NUL at buf[ret]. A file longer than buf_size-1 is silently truncated; compare f.size() first to detect truncation."""
if self.closed: return FRESULT.ERR_CLOSED
if not self.can_read: return FRESULT.ERR_PERM
if buf_size < 2: return FRESULT.ERR
file_size: LONGLONG = self.size()
if file_size < 0: return FRESULT.ERR_IO
to_read: ULONG = 0
if file_size < buf_size - 1:
to_read = ULONG(file_size)
else:
to_read = buf_size - 1
self.seek(0, SEEK_SET)
bytes_read: ULONG = 0
result: BOOL = w32.win32file.ReadFile(self.handle, buf, to_read, t.CPtr(&bytes_read), None)
if result == 0: return FRESULT.ERR_IO
if bytes_read < buf_size:
buf[bytes_read] = 0
return LONG(bytes_read)
def open(filename: str, mode: ULONG, share: ULONG = SHARE_READ) -> File*:
return File(filename, mode, share)
def openw(filename: w32.win32base.LPCWSTR, mode: ULONG, share: ULONG = SHARE_READ) -> FileW*:
return FileW(filename, mode, share)