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 顺序回归通过。
This commit is contained in:
TermiNexus committed 2026-10-05 16:18:44 +08:00
1 parent 27121f56a7
commit e71daeea0e
75 files changed
+3040 -4214

No files matched your search

+3 -6
View File
@@ -29,7 +29,7 @@ class dict:
d: dict = dict(mb)
d.set_int("x", 42)
p: t.CPtr = d["x"]
v: int = c.Deref((int*)(p)) # int 值按 i32 存,读回须给 int* 视图
v: int = c.Deref((int*)(p)) # int value stored as i32; read it back with an int* view
# Nested dict
inner: dict = dict(mb)
@@ -60,11 +60,8 @@ class dict:
def _find(self, key: str) -> sizet:
"""Find index of target key; returns __count__ if not found"""
for i in range(self.__count__):
# 元素地址:`T*(base + byte_offset)`。裸指针 rawptr 即 i8*,其上的 `+`
# 按 **1 字节**缩放 ⇒ 与原先 `uint64(base) + n` 再强转回指针逐字等价,
# 故去掉整数往返(与 _list.vp 同口径)。
# 下面 __getitem__/_set_variant_raw/get/get_vtype/__next__/to_json 同此式,
# 不再重复注释。
# Element address: `T*(base + byte_offset)`. rawptr is i8* and `+` scales by 1 byte, byte-identical to the old uint64(base)+n form (no integer round-trip).
# Same form below (__getitem__/_set_variant_raw/get/get_vtype/__next__/to_json), not annotated again.
key_ptr: str* = str*(self.__keys__ + i * 8)
if string.strcmp(key_ptr[0], key) == 0: return i
return self.__count__
+2 -8
View File
@@ -47,14 +47,8 @@ class list[T]:
string.memcpy(new_data, self.__data__, self.__count__ * self.__elem_size__)
self.__data__ = new_data
self.__capacity__ = new_cap
# 元素地址:`T*(self.__data__ + i * elem_size)`。
# · rawptr 就是 i8*,i8* 上的 `+` 按 **1 字节**缩放(ExprGenOps 的指针算术分流
# → ExprGenBase.translate_ptr_arith 按 pointee 大小缩放)⇒ 与原先
# `uint64(ptr) + n` 再强转回指针**逐字等价**,故去掉整数往返。
# · 步长仍由 __elem_size__ **显式**给出,不能改成下标访问(`__data__[i]` 会按
# sizeof(T) 缩放):list[int](pool, 8) / list[str](pool, 64) 这类"按显式步长
# 当字节缓冲"的用法依赖它。
# 下面 get/set/__getitem__/__setitem__/pop/__next__ 同此式,不再重复注释。
# Element address: `T*(self.__data__ + i * elem_size)`. rawptr is i8* and `+` scales by 1 byte, byte-identical to the old uint64(ptr)+n form (no integer round-trip).
# The stride is explicit via __elem_size__; do not use indexing (`__data__[i]` scales by sizeof(T)) since list[int](pool, 8) / list[str](pool, 64) rely on the explicit byte stride. Same form below (get/set/__getitem__/__setitem__/pop/__next__).
elem_ptr: T* = T*(self.__data__ + self.__count__ * self.__elem_size__)
*elem_ptr = item
self.__count__ += 1
+1 -1
View File
@@ -23,7 +23,7 @@ class Variant:
# Retrieve value
ptr: rawptr = v.ptr
i: int = *((int*)(ptr)) # int 值按 i32 存,读回须给 int* 视图
i: int = *((int*)(ptr)) # int values are stored as i32, so read them back through an int* view
"""
vtype: int
ptr: rawptr
+4 -8
View File
@@ -27,9 +27,8 @@ _tls_index: ulong = 0
def _init_tls():
"""Initialize TLS index (executes once; subsequent calls are skipped)."""
global _tls_index
# 双保险:TPV 已正式实现 @t.TLS(函数级一次性守卫,首次执行后置
# __tls_init_done 标志,后续调用直接返回),此判重守卫保留以兼容
# 尚未重建的旧 TPV.exe。_tls_index 初始为 0(TlsAlloc 成功不会返回 0)。
# Double guard: TPV now implements @t.TLS (a per-function one-shot guard setting __tls_init_done), but this re-check is kept for compatibility with older TPV.exe builds.
# _tls_index starts at 0 (TlsAlloc never returns 0 on success).
if _tls_index == 0:
_tls_index = TlsAlloc()
@@ -67,11 +66,8 @@ def _find_provider(field_name: str) -> rawptr:
"""Search for matching field_name starting from stack top.
Return associated pointer, or NULL if no match found."""
stack: _ProviderStack* = _get_stack()
# 上界显式转 t.CInt(有符号):本循环是「递减 + stop 为 -1」形态,若上界
# 被当作无符号(stack.top 是 uint64 字段),比较谓词会退化为 ugt,而 -1 在
# 无符号域是 UINT32_MAX ⇒ 条件恒假、循环体不可达、本函数恒返回 NULL(with 的
# __mbuddy__ 注入全部失效,_str.upper 对 NULL 取 vtable 崩溃)。显式转换让
# 符号域与递减语义一致,与编译器侧「stop 为负字面量时退回有符号谓词」互为双保险。
# Upper bound explicitly cast to t.CInt (signed): the loop is "decrement, stop at -1". If the bound were unsigned (stack.top is a uint64 field), the predicate would degrade to ugt and -1 (= UINT32_MAX unsigned) would make it always false, so the loop body is unreachable and this function always returns NULL (all with-injections break, and _str.upper crashes taking a vtable from NULL).
# The explicit cast keeps the signed domain consistent with the decrement semantics.
for i in range(int(stack.top) - 1, -1, -1):
# Content comparison: every string literal gets its own constant, so the
# push/find pointers differ and pointer equality never matches.
+4 -6
View File
@@ -284,9 +284,8 @@ class ArgumentParser:
arg: Argument* = None
value: str = None
# argv 是 char**(`str | t.CPtr`),argv[idx] 直接给出该位置的 char*。
# 不要写成 `str*(uint64(argv) + idx*8)`:那样右值是 i8**,TPC 的赋值路径
# 会对"指针的指针"右值再解引用一次(多 load 一层)→ 取到野指针。
# argv is char** (`str | t.CPtr`); argv[idx] directly gives the char* at that position.
# Do NOT write `str*(uint64(argv) + idx*8)`: the right-hand side would be i8**, and TPC's assignment path dereferences a "pointer to pointer" rvalue one extra time (an extra load) -> a wild pointer.
while idx < argc:
token = argv[idx]
if token is None:
@@ -434,9 +433,8 @@ def release(args: rawptr[ParsedArgs]):
if args is None: return
if args.__mbuddy__ is None: return
if args._ht is not None:
# 用临时变量拆分链式属性访问,避免 args._ht.__slots__ 跨模块类型推断失败
# 注意:类型注解必须用 hashtable.HashTable(带模块前缀),
# 否则编译器无法解析 HashTable 类型,导致 ht.__slots__ 属性访问失败
# Split the chained attribute access with a temp variable to avoid cross-module type inference failure for args._ht.__slots__.
# Note: the type annotation must be hashtable.HashTable (module-prefixed), otherwise the compiler cannot resolve HashTable and ht.__slots__ access fails.
ht: hashtable.HashTable* = args._ht
if ht.__slots__ is not None:
args.__mbuddy__.free(ht.__slots__)
+1 -1
View File
@@ -98,7 +98,7 @@ def parse_expression(src: str, pool: rawptr[memhub.MemManager]) -> rawptr[Expres
pool: memhub.MemManager instance, to allocate AST nodes
Returns:
Expression node (包装节点,body 字段为实际表达式), or None
Expression node (wrapper; body holds the actual expression), or None
"""
if src is None or pool is None: return None
_init_tables(pool)
+9 -12
View File
@@ -42,7 +42,7 @@ enum ASTKind:
MatchCase, MatchValue
MatchSingleton, MatchSequence, MatchMapping
MatchClass, MatchStar, MatchAs, MatchOr
# Soft keyword statement(append 在末尾以保持既有枚举值不变)
# Soft keyword statement (appended last to keep existing enum values unchanged)
FallThrough
@@ -364,26 +364,23 @@ virtual class AST:
# ============================================================
# Stmt - Statement Base Class
#
# 所有语句节点(Assign/If/For/While/Return/...)继承 Stmt。
# isinstance(node, ast.Stmt) 可在运行时区分"语句"与"表达式"
# (依赖 TPV isinstance 子类集合判定 + 三层 vtable 继承)。
# 不 override kind()(继承 AST 的 0),具体节点各自返回 ASTKind 值。
# All statement nodes (Assign/If/For/While/Return/...) inherit Stmt.
# isinstance(node, ast.Stmt) distinguishes statements from expressions at runtime.
# Does not override kind() (inherits AST's 0); concrete nodes return their ASTKind.
# ============================================================
virtual class Stmt(AST):
"""语句节点基类(Assign/If/For/While/Return/...)"""
"""Statement node base class (Assign/If/For/While/Return/...)"""
# ============================================================
# ExprNode - Expression Base Class
#
# 所有表达式节点(Name/Constant/Call/BinOp/...)继承 ExprNode。
# isinstance(node, ast.ExprNode) 可在运行时区分"表达式"与"语句"。
#
# 命名说明:不能叫 Expr——ASTKind.Expr(base.vp L23)与 stmts.vp 的
# Expr 类已是"表达式语句"(Expression Statement)的既有语义。
# All expression nodes (Name/Constant/Call/BinOp/...) inherit ExprNode.
# isinstance(node, ast.ExprNode) distinguishes expressions from statements at runtime.
# Not named Expr: ASTKind.Expr and the stmts.vp Expr class already mean expression statement.
# ============================================================
virtual class ExprNode(AST):
"""表达式节点基类(Name/Constant/Call/BinOp/...)"""
"""Expression node base class (Name/Constant/Call/BinOp/...)"""
# ============================================================
+10 -14
View File
@@ -51,25 +51,25 @@ class Name(ExprNode):
def name_get_id(node: rawptr[Name]) -> str:
"""获取 Name.id 字段(跨模块访问器,stub 字段访问陷阱防护)"""
"""Get Name.id field (cross-module accessor; guards against stub field-access pitfalls)"""
if node is None: return None
return node.id
def constant_get_str_val(node: rawptr[Constant]) -> str:
"""获取 Constant.str_val 字段(跨模块访问器)"""
"""Get Constant.str_val field (cross-module accessor)"""
if node is None: return None
return node.str_val
def binop_get_left(node: rawptr[BinOp]) -> rawptr[AST]:
"""获取 BinOp.left 字段(跨模块访问器)"""
"""Get BinOp.left field (cross-module accessor)"""
if node is None: return None
return node.left
def binop_get_right(node: rawptr[BinOp]) -> rawptr[AST]:
"""获取 BinOp.right 字段(跨模块访问器)"""
"""Get BinOp.right field (cross-module accessor)"""
if node is None: return None
return node.right
@@ -284,9 +284,8 @@ class Call(ExprNode):
func: rawptr[AST]
args: rawptr[list[rawptr[AST]]]
keywords: rawptr[list[rawptr[AST]]]
# 值位置 `volatile f()` / `volatile S()` 前缀标记(见 parser.vp _IsVolatileCallPrefix):
# 1=本次调用的结果槽(赋值目标/临时槽)按 volatile 存取,结构体构造的写入亦带
# LLVM volatile。追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入。
# Value-position `volatile f()` / `volatile S()` prefix marker (see _IsVolatileCallPrefix):
# 1 = result/temp slot accessed volatile (struct writes too). Appended last: offsets unchanged.
IsVolatile: int
def __new__(self, pool: rawptr[memhub.MemManager],
@@ -931,20 +930,17 @@ class Attribute(ExprNode):
# ============================================================
# 字段访问器(跨模块 stub 字段访问陷阱防护)
#
# 其他模块(如 TypeGenCType)直接访问 ast.Attribute 的字段时,
# TPC 可能按 stub 类型生成错误 GEP(读垃圾地址 → 0xC0000005 崩溃)。
# 本模块是完整类型,字段偏移正确;跨模块调用方用这些访问器。
# Field accessors (guard against cross-module stub field-access pitfalls): other modules reading
# ast.Attribute fields directly may get a wrong stub GEP and crash, so they use these accessors.
# ============================================================
def attribute_get_value(node: rawptr[Attribute]) -> rawptr[AST]:
"""获取 Attribute.value 字段(跨模块访问器)"""
"""Get Attribute.value field (cross-module accessor)"""
if node is None: return None
return node.value
def attribute_get_attr(node: rawptr[Attribute]) -> str:
"""获取 Attribute.attr 字段(跨模块访问器)"""
"""Get Attribute.attr field (cross-module accessor)"""
if node is None: return None
return node.attr
+39 -62
View File
@@ -37,25 +37,25 @@ _g_lex_err_msg: str = None
def get_lex_err_line() -> int:
"""最近一次词法层 fail-fast 的行号(0=无)
"""Line number of the latest lexer fail-fast (0 = none).
供 parser.parse_tokens 取出后转写解析错误通道(_g_parse_err_*)。
Consumed by parser.parse_tokens and forwarded to the parse error channel.
"""
return _g_lex_err_line
def get_lex_err_col() -> int:
"""最近一次词法层 fail-fast 的列号(0-based;-1 表示无列信息)"""
"""Column of the latest lexer fail-fast (0-based; -1 = no column info)"""
return _g_lex_err_col
def get_lex_err_msg() -> str:
"""最近一次词法层 fail-fast 的消息(含字面量原文 / 超出的位宽 / 上限 / 修法建议)"""
"""Message of the latest lexer fail-fast (literal / required width / limit / fix hint)"""
return _g_lex_err_msg
def _lex_reset_err():
"""清空词法层 fail-fast 三元组(每次 tokenize 入口调用,避免跨文件串用旧错误)"""
"""Clear the lexer fail-fast triple (called at each tokenize entry to avoid stale errors)"""
global _g_lex_err_line, _g_lex_err_col, _g_lex_err_msg
_g_lex_err_line = 0
_g_lex_err_col = 0
@@ -63,10 +63,8 @@ def _lex_reset_err():
def _lex_record_overflow(text: str, lineno: int, col: int):
"""记录整数字面量溢出(fail-fast;只记首次)。
报文含:字面量原文 + **超出的位宽(至少 65 位,上限 2^64-1)** + 修法建议;
文件:行:列 与源码行由调用方(parser.parse_tokens → dump_parse_traceback)补全。
"""Record an integer literal overflow (fail-fast; first record only); message carries literal
+ required width (>= 65 bits, limit 2^64-1) + fix hint (file:line:col filled later by parse_tokens).
"""
global _g_lex_err_line, _g_lex_err_col, _g_lex_err_msg
if _g_lex_err_line != 0:
@@ -88,25 +86,16 @@ def _lex_record_overflow(text: str, lineno: int, col: int):
def _lex_int_exceeds_u64(text: str) -> int:
"""整数字面量文本的幅值是否超出 64 位位模式上限 2^64-1(1=超出,0=未超出)。
纯文本判定(与 AssignGenMixin._int_text_exceeds_64 同口径;**不落进任何定宽
容器、不回绕后再比较**)。只处理整数字面量(调用方保证 is_float==0);字面量
文本不含正负号(一元加/减是独立 token,不属于字面量本身)。
十进制:去 '_' 与前导零后,与 '18446744073709551615'(2^64-1,20 位)按
长度 + 字典序比较(同位数时字典序 = 数值序)。
0x/0X :有效位数 > 16 ⇒ 超出(16 位十六进制的最大值为 2^64-1)。
0o/0O :有效位数 > 22 ⇒ 超出;== 22 位且首位 > '1' ⇒ 超出
(8^21 = 2^63 ⇒ 22 位以 '1' 开头恰为 2^64-1 为上限)。
0b/0B :有效位数 > 64 ⇒ 超出。
"""True if the integer literal magnitude exceeds the 64-bit limit 2^64-1 (1 = yes, 0 = no).
Pure text compare, no wrap-around: decimal length+lexicographic vs "18446744073709551615",
0x > 16, 0o > 22 (or 22 leading > '1'), 0b > 64 (literal text carries no sign).
"""
if text is None:
return 0
tl: sizet = string.strlen(text)
if tl == 0:
return 0
# 进制判定(前缀大小写皆可)
# Base detection (prefix letter case-insensitive)
base: int = 10
start: sizet = 0
if tl >= 2 and text[0] == '0':
@@ -120,7 +109,7 @@ def _lex_int_exceeds_u64(text: str) -> int:
elif pfx == 'b' or pfx == 'B':
base = 2
start = 2
# 统计有效位数(跳过 '_' 与前导零),并记下首个有效位
# Count significant digits (skip '_' and leading zeros) and remember the first one
sig: int = 0
first: char = '0'
j: sizet = start
@@ -146,11 +135,11 @@ def _lex_int_exceeds_u64(text: str) -> int:
break
if sig == 0:
if ch == '0':
continue # 前导零不计
continue # leading zeros not counted
first = ch
sig += 1
if sig == 0:
return 0 # 值 0
return 0 # value 0
if base == 16:
return 1 if sig > 16 else 0
if base == 2:
@@ -161,13 +150,13 @@ def _lex_int_exceeds_u64(text: str) -> int:
if sig == 22 and first > '1':
return 1
return 0
# 十进制:有效位数与 2^64-1 的文本长度比较
# Decimal: compare significant-digit count against the 2^64-1 text length
mlen: sizet = 20
if sig > mlen:
return 1
if sig < mlen:
return 0
# 等长(20 位):逐位与 '18446744073709551615' 字典序比较
# Equal length (20 digits): lexicographic compare against '18446744073709551615'
max_text: str = "18446744073709551615"
d: sizet = 0
k: sizet = start
@@ -193,17 +182,15 @@ def _lex_int_exceeds_u64(text: str) -> int:
def _lex_mag_digit_val(ch: char, base: int) -> int:
"""数字字符 → 位值(十六进制含 a-f/A-F;其余进制为 '0'..基-1)。"""
"""Digit char -> positional value (hex accepts a-f/A-F; other bases are '0'..base-1)."""
if base == 16:
return _hex_val(ch)
return int(ch) - 48
def _lex_mag_gt_text(text: str, base: int, start: sizet, sig: int, max_text: str) -> int:
"""整数字面量文本的有效数字是否 > 同位数上限文本(1=大于,0=不大于)。
两侧均跳过 '_' 与前导零,逐位按**位值**比较(十六进制用 _hex_val,其余进制
字符序即位值序);纯文本判定,不落定宽容器、不回绕。
"""True if the literal's significant digits exceed the same-length max text (1 = yes).
Both sides skip '_'/leading zeros, compared by positional value (hex via _hex_val); pure text.
"""
d: sizet = 0
k: sizet = start
@@ -240,16 +227,9 @@ def _lex_mag_gt_text(text: str, base: int, start: sizet, sig: int, max_text: str
def _lex_int_neg_exceeds_i64(text: str) -> int:
"""整数字面量幅值是否**严格大于** 2^63(1=超出,0=未超出)。
用于"一元减 + 整数字面量"的联合越界判定:`-` 是独立的一元运算符、不属于
字面量,故幅值合法的字面量(≤ 2^64-1,`_lex_int_exceeds_u64` 已放行)在取负
后可能 < -2^63。幅值恰为 2^63 时一元减结果 = INT64_MIN = -9223372036854775808
(合法),故本判据用**严格大于**。
纯文本判定(与 _lex_int_exceeds_u64 同口径,不落定宽容器、不回绕)。
2^63 的幅值文本:十进制 19 位 `9223372036854775808`;0x 16 位
`8000000000000000`;0o 22 位 `1000000000000000000000`;0b 64 位(1 + 63 个 0)。
"""True if the magnitude is strictly greater than 2^63 (1 = yes).
For unary-minus + literal overflow: a magnitude-valid literal may become < -2^63 after
negation; exactly 2^63 is legal (INT64_MIN). Pure text compare, no wrap-around.
"""
if text is None:
return 0
@@ -292,10 +272,10 @@ def _lex_int_neg_exceeds_i64(text: str) -> int:
if ok_digit == 0:
break
if sig == 0 and ch == '0':
continue # 前导零不计
continue # leading zeros not counted
sig += 1
if sig == 0:
return 0 # 值 0
return 0 # value 0
if base == 16:
if sig > 16: return 1
if sig < 16: return 0
@@ -339,7 +319,7 @@ class Lexer:
def __new__(self, pool: rawptr[memhub.MemManager]):
ptr: rawptr[Lexer] = pool.alloc(Lexer.__sizeof__())
if ptr == None:
# 池耗尽时不再静默返回 NULL(下游会把 NULL 当有效 Lexer 用 → 崩在任意位置)
# Pool exhausted: fail-fast instead of returning NULL (downstream would treat NULL as a valid Lexer)
stdio.printf("[AST] Lexer 分配失败:内存池已耗尽(need=%zu 字节)\n",
sizet(Lexer.__sizeof__()))
stdio.fflush(None)
@@ -437,7 +417,7 @@ def _is_hex(ch: char) -> int:
def _hex_val(ch: char) -> int:
"""十六进制字符转数值 0-15(供 \xNN 转义解析)"""
"""Hex char -> value 0-15 (for \xNN escape parsing)"""
if ch >= '0' and ch <= '9': return int(ch) - 48
if ch >= 'a' and ch <= 'f': return int(ch) - 87
if ch >= 'A' and ch <= 'F': return int(ch) - 55
@@ -723,8 +703,8 @@ def _emit_number(lx: rawptr[Lexer], start: sizet,
if is_float:
tok.float_val = string.atof(tok.str_val)
else:
# 整数字面量幅值 > 2^64-1:词法层 fail-fast(纯文本判定,不静默回绕)。
# 合法区间 0..2^64-1 仍按 64 位位模式存入 int64(产物形态不变)。
# Integer literal magnitude > 2^64-1: lexer fail-fast (pure text, no silent wrap).
# Range 0..2^64-1 is still stored into int64 as a 64-bit bit pattern (unchanged shape).
if _lex_int_exceeds_u64(tok.str_val) != 0:
_lex_record_overflow(tok.str_val, start_lineno, start_col)
tok.int_val = string.atoll(tok.str_val)
@@ -794,7 +774,7 @@ def _read_string(lx: rawptr[Lexer]):
# Read string content
content_buf_start: sizet = lx.pos
# f-string {} 表达式块嵌套深度(PEP 701 同引号嵌套字符串支持用)
# f-string {} expression block nesting depth (for PEP 701 same-quote nested strings)
fstr_depth: int = 0
while lx.pos < lx.len:
ch = lx.src[lx.pos]
@@ -822,7 +802,7 @@ def _read_string(lx: rawptr[Lexer]):
lx.col += 1
else:
if is_fstring:
# {{ 与 }} 是转义字面量,不参与深度计数
# {{ and }} are escaped literals, not counted in depth
if ch == '{' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '{':
lx.pos += 2
lx.col += 2
@@ -843,8 +823,8 @@ def _read_string(lx: rawptr[Lexer]):
lx.col += 1
continue
if fstr_depth > 0 and (ch == '\'' or ch == '"'):
# {} 表达式块内允许嵌套字符串字面量(PEP 701 同引号嵌套):
# 跳过到匹配引号,块内引号不终止外层 f-string。
# Nested string literals allowed inside {} blocks (PEP 701 same-quote nesting):
# skip to the matching quote; inner quotes do not terminate the outer f-string.
nq: char = ch
lx.pos += 1
lx.col += 1
@@ -859,7 +839,7 @@ def _read_string(lx: rawptr[Lexer]):
lx.col += 1
break
if c2 == '\n':
# 单行字符串不能跨行
# single-line string cannot span lines
lx.error_count += 1
return
lx.pos += 1
@@ -938,10 +918,8 @@ def _read_string(lx: rawptr[Lexer]):
elif next_ch == '0':
conv_buf[wpos] = '\0'
elif next_ch == 'x' and rpos + 2 < content_len:
# \xNN 十六进制转义(对齐 Python):'\\x1b' → 单字节 0x1B。
# 此前当作未知转义保留('\\'+'x' 2 字节)→ esc_len≠1 →
# '\x1b' 被判为 CONST_STR 而非 CONST_CHAR(27) → 字符数组
# 元素提取失败(desktop _ps2_set1 索引 1 恒 0 根因)。
# \xNN hex escape (matches Python): '\\x1b' -> single byte 0x1B.
# Must yield esc_len == 1 so '\xNN' becomes CONST_CHAR, not CONST_STR.
h1: char = lx.src[content_buf_start + rpos + 1]
h2: char = lx.src[content_buf_start + rpos + 2]
if _is_hex(h1) != 0 and _is_hex(h2) != 0:
@@ -950,7 +928,7 @@ def _read_string(lx: rawptr[Lexer]):
wpos += 1
rpos += 3
continue
# 非法十六进制('\x' 后不足 2 位 hex):保留原样
# Invalid hex (\x followed by fewer than 2 hex digits): keep as-is
conv_buf[wpos] = '\\'
wpos += 1
conv_buf[wpos] = next_ch
@@ -1072,9 +1050,8 @@ def tokenize(lx: rawptr[Lexer]) -> rawptr[Token]:
continue
# Backslash continuation line
# 支持 LF 与 CRLF(Windows 文件行尾):CRLF 下 `\` 后是 '\r'(非 '\n'),
# 只查 '\n' 会失效 → `\` 被当未知字符跳过、'\n' 产生 NEWLINE → 表达式
# 中断(VpiReader.vp L324 `or \` 行尾续行根因,CRLF 文件)。
# Support LF and CRLF line endings: under CRLF `\` is followed by '\r' (not '\n'),
# so checking only '\n' fails and the backslash continuation breaks.
if ch == '\\' and lx.pos + 1 < lx.len:
if lx.src[lx.pos + 1] == '\n':
lx.pos += 2
@@ -1082,7 +1059,7 @@ def tokenize(lx: rawptr[Lexer]) -> rawptr[Token]:
lx.col = 0
continue
if lx.src[lx.pos + 1] == '\r':
# \ + \r\n:跳 \、\r、\n
# \ + \r\n: skip \, \r, \n
lx.pos += 2
if lx.pos < lx.len and lx.src[lx.pos] == '\n':
lx.pos += 1
+422 -708
View File
File diff suppressed because it is too large. Load diff
+8 -10
View File
@@ -294,8 +294,8 @@ class FunctionDef(Stmt):
decorator_list: list[rawptr[AST]], returns: AST, flags: CInt,
type_params: rawptr[list[str]] = None)
type_params: 泛型函数类型参数名列表(PEP 695 def f[T](...): 中的 [T]),
None 表示非泛型函数。"""
type_params: generic function type-parameter name list (the [T] in PEP 695 def f[T](...)),
None means a non-generic function."""
name: str
args: rawptr[AST]
decorator_list: rawptr[list[rawptr[AST]]]
@@ -474,8 +474,8 @@ class ClassDef(Stmt):
"""ClassDef(name: str, bases: list[rawptr[AST]], keywords: list[rawptr[AST]],
decorator_list: list[rawptr[AST]], type_params: rawptr[list[str]])
type_params: 泛型类型参数名列表(PEP 695 class list[T]: 中的 [T]),
None 表示非泛型类。"""
type_params: generic type-parameter name list (the [T] in PEP 695 class list[T]:),
None means a non-generic class."""
name: str
bases: rawptr[list[rawptr[AST]]]
keywords: rawptr[list[rawptr[AST]]]
@@ -982,11 +982,11 @@ class Continue(Stmt):
# ============================================================
# FallThrough Node(关键字 `fallthrough`,仅 switch 的 case 体内合法)
# FallThrough Node (keyword `fallthrough`, legal only inside a switch case body)
# ============================================================
class FallThrough(Stmt):
"""FallThrough() —— switch 的 case 体不自动 break,落到下一个 case 体"""
"""FallThrough() - a switch case body does not auto-break, so it falls through to the next case"""
def __new__(self, pool: rawptr[memhub.MemManager]):
ptr: rawptr[FallThrough] = pool.alloc(FallThrough.__sizeof__())
@@ -1092,10 +1092,8 @@ class ImportFrom(Stmt):
class Match(Stmt):
"""Match(subject: AST, cases: list[rawptr[AST]], is_switch: int)
is_switch=1 表示本节点由 `switch <expr>:` 语法糖 desugar 而来(见 parser.vp 的
_parse_switch):语义是 C 的 switch —— case 默认不穿透,case 体内写
`fallthrough` 才落到下一个 case。is_switch=0 为真正的 `match` 语句,
**完全禁止 fallthrough**(写出即 fail-fast)。
is_switch=1: from `switch <expr>:` sugar (see _parse_switch), C semantics (no fall-through without
`fallthrough`). is_switch=0: real `match`, fallthrough is completely forbidden (fail-fast).
"""
subject: rawptr[AST]
cases: rawptr[list[rawptr[AST]]]
+45 -77
View File
@@ -7,12 +7,8 @@ import stdlib
def _ast_pool_fatal(what: str, need: sizet):
"""池分配失败(alloc 返回 NULL)时的统一 fail-fast。
此前这些 alloc 失败只是静默返回,留下 str_val=NULL / 哑 token;后续
`_kw_lookup(tok.str_val)` → `_kw_hash_str` 在 NULL 上取字节,崩成
ASAN "access-violation on unknown address 0x0",把真因(内存池耗尽)
掩盖成空指针崩溃。这里直接报真因并终止。
"""Unified fail-fast when a pool alloc fails (returns NULL).
Reports the real cause (pool exhausted) and terminates instead of leaving NULL str_val / dummy tokens.
"""
stdio.printf("[AST] %s 分配失败:内存池已耗尽(alloc 返回 NULL,need=%zu 字节)"
"——上游有块被写穿/丢失,见 [MEM] 报告\n", what, need)
@@ -88,67 +84,46 @@ enum Keyword:
Match
Case
Type
# def 的别名关键字(fn/fun/func/function,语义与 def 完全相同)
# def alias keywords (fn/fun/func/function, identical semantics to def)
Fn
Fun
Func
Function
# extern 函数声明关键字(FFI,等价 t.State 标记;软判定见 _is_funcdef_kw)
# extern function declaration keyword (FFI, equivalent to t.State; soft test in _is_funcdef_kw)
Extern
# export 函数声明关键字(等价 t.CExport,裸名导出不加 SHA1 头;软判定见 _is_funcdef_kw)
# export function declaration keyword (equivalent to t.CExport; soft test in _is_funcdef_kw)
Export
# define 宏函数关键字(与 def 语义一致,返回注解自动追加 t.CDefine 标记
# 为宏函数;软判定见 _is_funcdef_kw)
# define macro-function keyword (same semantics as def, return annotation gets t.CDefine;
# soft test in _is_funcdef_kw)
Define
# enum 枚举定义关键字(语法糖:`enum X:` 与 `class X(t.CEnum):` 等价,
# 见 parser.vp 的 _parse_enumdef)。追加在**表尾**而非插在 Class 之后:
# 本枚举成员的值按声明序递增,插在中间会平移 Class 之后所有关键字的编号
# (kw_subtype 数值),追加则既有编号逐字不变。
# enum definition keyword (sugar: `enum X:` == `class X(t.CEnum):`, see _parse_enumdef).
# Appended at the enum tail so existing kw_subtype values stay unchanged.
Enum
# typedef 类型别名关键字(语法糖:`typedef T1 = T2` 与
# `T1: t.CTypedef = T2` 等价,见 parser.vp 的 _parse_typedef)。同样追加在
# **表尾**:既有关键字编号(kw_subtype)逐字不变。
# typedef alias keyword (sugar: `typedef T1 = T2` == `T1: t.CTypedef = T2`, see _parse_typedef).
# Appended at the enum tail: existing kw_subtype values unchanged.
Typedef
# 类型描述符关键字 const / volatile(语法糖:描述符写在类型前,
# `a: const int` 与 `a: t.CConst | int` 等价,见 parser.vp 的 _parse_type_ann)。
# 同样追加在**表尾**:既有关键字编号(kw_subtype)逐字不变。
# 二者是**软关键字**——只在注解位置(`:` 后 / `->` 后)生效;作标识符的写法
# (t.attr.const(),TransPyV/Test/App/attr_test.vp:142)不受影响:
# 该处 token 类型仍是 Name,属性名解析只看 TokenType.Name,不看 kw_subtype。
# const/volatile descriptor keywords (sugar: `a: const int` == `a: t.CConst | int`, _parse_type_ann),
# appended at the enum tail. Soft keywords: only in annotation position; `t.attr.const()` unaffected.
Const
Volatile
# virtual / novirtual 修饰符关键字(语法糖:`virtual class C:` /
# `novirtual class C:` / `virtual def m(...)` / `novirtual def m(...)`,
# 与手写类/方法装饰器 `@t.CVTable` / `@t.NoVTable` **同一套表示**,
# 见 parser.vp 的 _ParseVirtualModified)。同样追加在**表尾**:既有关键字
# 编号(kw_subtype)逐字不变。
# 二者是**软关键字**——只在"语句起始 + 紧跟 class/def"时生效;作标识符的写法
# (`x = d.virtual` / 形参名 / for 目标 / 字典键)不受影响:那些位置按
# TokenType.Name 解析,不看 kw_subtype。
# virtual/novirtual modifier keywords (sugar for @t.CVTable / @t.NoVTable, _ParseVirtualModified),
# appended at the enum tail. Soft keywords: only at statement start before class/def; identifiers unaffected.
Virtual
Novirtual
# union 类型定义关键字(语法糖:`union X:` 与 `class X(t.CUnion):` 等价,
# 见 parser.vp 的 _parse_uniondef)。同样追加在**表尾**:既有关键字编号
# (kw_subtype)逐字不变;全仓 .vp 源码中 `union` 不作标识符使用。
# union definition keyword (sugar: `union X:` == `class X(t.CUnion):`, see _parse_uniondef),
# appended at the enum tail; `union` is never used as an identifier in the repo.
Union
# renum 变体类型定义关键字(语法糖:`renum X:` 与 `class X(t.REnum):` 等价,
# 见 parser.vp 的 _parse_renumdef)。同样追加在**表尾**:既有关键字编号
# (kw_subtype)逐字不变;全仓 .vp 源码中 `renum` 不作标识符使用。
# renum variant-type keyword (sugar: `renum X:` == `class X(t.REnum):`, see _parse_renumdef),
# appended at the enum tail; `renum` is never used as an identifier in the repo.
Renum
# struct 纯数据类型定义关键字(语法糖:`struct X:` 与
# `class X(t.CStruct):` + `@t.NoVTable` 等价,见 parser.vp 的 _parse_structdef):
# 与 class 同形,但**只能表示数据类型**——禁止 virtual/novirtual 修饰、禁止类体内
# 定义 def。同样追加在**表尾**:既有关键字编号(kw_subtype)逐字不变;
# 全仓 .vp 源码中 `struct` 不作标识符使用(仅出现在注释/字符串与
# build_gep_struct 之类复合标识符里)。
# struct data-type keyword (sugar: `struct X:` == `class X(t.CStruct):` + @t.NoVTable, _parse_structdef),
# appended at the enum tail. Data-only: no virtual/novirtual, no def in body; never an identifier.
Struct
# switch 多分支语句关键字(`switch <expr>:` + `case <常量>: ` / `default:`,
# 见 parser.vp 的 _parse_switch)。C switch 语义:默认**不穿透**,case 体内
# 写 `fallthrough` 才落到下一个 case。同样追加在**表尾**:既有关键字编号
# (kw_subtype)逐字不变;全仓 .vp 源码中 `switch` 不作标识符使用。
# switch statement keyword (`switch <expr>:` + `case`/`default`, see _parse_switch), appended
# at the enum tail. C switch semantics: no fall-through unless `fallthrough` is written.
Switch
# fallthrough 语句关键字(仅在 switch 的 case 体内合法;match 内使用报错)。
# 追加在**表尾**:既有关键字编号(kw_subtype)逐字不变。
# fallthrough statement keyword (legal only inside a switch case body; error inside match),
# appended at the enum tail.
FallThrough
@@ -257,21 +232,18 @@ class KwEntry:
_kw_head: rawptr[KwEntry] = None
_kw_pool: rawptr[memhub.MemManager] = None
# 关键字哈希索引(开放寻址 | 线性探测):槽为 u64,0=空槽,否则存 KwEntry 地址。
# 目的:把 _kw_lookup 的 46 项链表线性扫描降为期望 O(1)(不改链表本身)。索引与
# 链表同源——在 _init_keywords 里由同一批 _kw_intern 派生,不另抄关键字列表。
# 关键字为静态集合且一次性注册完毕 ⇒ 索引是"完整"的:查找未命中等价于原扫描
# 返回 0(无需回退链表扫描);回退仅用于索引未建成(分配失败)时,保证正确性。
# Keyword hash index (open addressing, linear probing): slot u64, 0 = empty else KwEntry address;
# expected O(1) lookups, falling back to the list only when not built (alloc failure). Miss returns 0.
_kw_index: sizet* = None
# 索引槽数(2 的幂且 > 2*46,负载 < 0.5,探测必然终止)
# Index slot count (power of two > 2*46, load < 0.5 so probing always terminates)
define _KW_IDX_CAP = 128
# FNV-1a 64 位常量(与 hashtable.vp 同款)
# FNV-1a 64-bit constants (same as hashtable.vp)
_KW_FNV_OFFSET: uint64 = 0xcbf29ce484222325
_KW_FNV_PRIME: uint64 = 0x100000001b3
def _kw_hash_str(key: str) -> uint64:
"""关键字名 FNV-1a 64 位哈希(同一键必得同一值;仅用于槽定位)"""
"""FNV-1a 64-bit hash of a keyword name (same key -> same value; used only for slot location)"""
h: uint64 = _KW_FNV_OFFSET
k: str = key
while k[0] != 0:
@@ -282,11 +254,8 @@ def _kw_hash_str(key: str) -> uint64:
def _kw_index_put(entry: rawptr[KwEntry]):
"""把关键字条目写入哈希索引(索引未建成时静默跳过)
同键只保留首注册者(与查找"命中即返回"语义一致);冲突逐键 strcmp 区分,
不能只比哈希——否则不同关键字撞槽会返回错误 kw_id。
"""
"""Write a keyword entry into the hash index (silently skipped if not built); a duplicate key keeps
the first registrant, and collisions are separated by strcmp (hash alone could return a wrong kw_id)."""
if _kw_index == None: return
h: uint64 = _kw_hash_str(entry.name)
mask: sizet = sizet(_KW_IDX_CAP - 1)
@@ -316,18 +285,17 @@ def _kw_intern(pool: rawptr[memhub.MemManager], name: str, kw_id: int):
entry.kw_id = kw_id
entry.next = _kw_head
_kw_head = entry
# 同步写入哈希索引(关键字集合的唯一真源仍是上面的链表)
# Also write to the hash index (the linked list above is still the single source of truth)
_kw_index_put(entry)
def _kw_lookup(name: str) -> int:
"""Look up keyword name, return the kw_id (0 if not keyword)
"""Look up keyword name, return the kw_id (0 if not a keyword).
快速路径:查哈希索引(期望 O(1),不分配内存);索引未建成时回退原链表扫描。
Fast path: hash index (expected O(1), no allocation); falls back to the linked-list scan if not built.
"""
if name == None:
# 防御:str_val 为 NULL 时 _kw_hash_str 会在 NULL 上取字节,崩成
# ASAN "access-violation on unknown address 0x0",真因(池耗尽)被掩盖。
# Defensive: a NULL str_val would crash _kw_hash_str and mask the real cause (pool exhausted).
stdio.printf("[AST] _kw_lookup 收到 NULL 名称:token.str_val 未分配成功(内存池耗尽 / 未 set)\n")
stdio.fflush(None)
stdlib.exit(1)
@@ -358,7 +326,7 @@ def _init_keywords(pool: rawptr[memhub.MemManager]):
global _kw_head, _kw_pool, _kw_index
if _kw_head != None: return
_kw_pool = pool
# 建哈希索引槽数组(一次性分配;分配失败则 _kw_index 保持 None → 查找回退链表扫描)
# Allocate the index slot array once; on failure _kw_index stays None -> lookups fall back to the list
idx_buf: sizet* = pool.alloc(_KW_IDX_CAP * 8)
if idx_buf != None:
string.memset(idx_buf, 0, _KW_IDX_CAP * 8)
@@ -401,7 +369,7 @@ def _init_keywords(pool: rawptr[memhub.MemManager]):
_kw_intern(pool, "match", Keyword.Match)
_kw_intern(pool, "case", Keyword.Case)
_kw_intern(pool, "type", Keyword.Type)
# def 的别名关键字(fn/fun/func/function,语义与 def 完全相同)
# def alias keywords (fn/fun/func/function, identical semantics to def)
_kw_intern(pool, "fn", Keyword.Fn)
_kw_intern(pool, "fun", Keyword.Fun)
_kw_intern(pool, "func", Keyword.Func)
@@ -411,20 +379,20 @@ def _init_keywords(pool: rawptr[memhub.MemManager]):
_kw_intern(pool, "define", Keyword.Define)
_kw_intern(pool, "enum", Keyword.Enum)
_kw_intern(pool, "typedef", Keyword.Typedef)
# 类型描述符软关键字(仅注解位置生效,见 parser.vp 的 _parse_type_ann)
# Type-descriptor soft keywords (annotation position only, see _parse_type_ann)
_kw_intern(pool, "const", Keyword.Const)
_kw_intern(pool, "volatile", Keyword.Volatile)
# virtual / novirtual 修饰符软关键字(仅"语句起始 + 紧跟 class/def"生效,
# 见 parser.vp 的 _ParseVirtualModified)
# virtual/novirtual modifier soft keywords (statement start before class/def only,
# see _ParseVirtualModified)
_kw_intern(pool, "virtual", Keyword.Virtual)
_kw_intern(pool, "novirtual", Keyword.Novirtual)
_kw_intern(pool, "union", Keyword.Union)
# renum 变体类型定义关键字(语法糖,同 enum/union,见 parser.vp 的 _parse_renumdef)
# renum variant-type keyword (sugar, same as enum/union; see _parse_renumdef)
_kw_intern(pool, "renum", Keyword.Renum)
# struct 纯数据类型定义关键字(语法糖,同 enum/union/renum,见 parser.vp 的
# _parse_structdef:基类 t.CStruct + 伪装饰器 @t.NoVTable,禁 virtual/novirtual、禁 def)
# struct data-type keyword (sugar, same as enum/union/renum; see _parse_structdef:
# base t.CStruct + pseudo decorator @t.NoVTable, no virtual/novirtual and no def)
_kw_intern(pool, "struct", Keyword.Struct)
# switch / fallthrough 关键字(见 parser.vp 的 _parse_switch 与 _parse_small_stmt)
# switch / fallthrough keywords (see _parse_switch and _parse_small_stmt)
_kw_intern(pool, "switch", Keyword.Switch)
_kw_intern(pool, "fallthrough", Keyword.FallThrough)
+1 -1
View File
@@ -7,7 +7,7 @@ import t, c
# Base64 encoding lookup table
b64_tab: []char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# Base64 反向解码表,映射字符到索引值
# Base64 reverse decode table mapping characters to index values
b64_dec_tab: [80]int8 = [
62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+7 -8
View File
@@ -102,9 +102,8 @@ def init_binascii():
HEX_VALS[int(HEX_CHARS[i] - 32)] = i
for i in range(64):
B64_VALS[int(B64_CHARS[i])] = i
# HQX_VALS 由标准 BinHex 字母表(见 HQX_CHARS 静态初值,RFC 1741)反查建立。
# 注意:此处【不得】再重写 HQX_CHARS——此前用 `chr(32 + i)` 覆盖 0..63,
# 会把标准 BinHex 表整张冲掉,导致 b2a_hqx/a2b_hqx 与 RFC 1741 不兼容。
# HQX_VALS is reverse-mapped from the standard BinHex alphabet (HQX_CHARS, RFC 1741).
# Never overwrite HQX_CHARS here: it would break RFC 1741 compatibility of b2a_hqx/a2b_hqx.
for i in range(64):
HQX_VALS[uchar(HQX_CHARS[i])] = i
@@ -296,14 +295,14 @@ def b2a_hqx(data: str, length: int, crc: ushort = 0) -> tuple[str, ushort]:
out: str = str(stdlib.malloc(5))
if not out:
return None, crc
# 3字节 → 4字节 HQX
# Pack 3 source bytes into 4 HQX characters.
m: uint = (uchar(data[0]) << 16) | (uchar(data[1]) << 8) | uchar(data[2])
out[0] = HQX_CHARS[(m >> 18) & 0x3F]
out[1] = HQX_CHARS[(m >> 12) & 0x3F]
out[2] = HQX_CHARS[(m >> 6) & 0x3F]
out[3] = HQX_CHARS[m & 0x3F]
out[4] = '\0'
# CRC 初始化 + 正确计算
# Seed the CRC with the caller's value, then fold in the 3 source bytes.
s: ushort = crc
for i in range(3):
byte: uchar = uchar(data[i])
@@ -314,12 +313,12 @@ def b2a_hqx(data: str, length: int, crc: ushort = 0) -> tuple[str, ushort]:
def a2b_hqx(data: str, crc: ushort = 0) -> tuple[str, ushort]:
if not data:
return None, crc
# 检查字符是否有效
# Reject the input as soon as any character is not a valid HQX symbol.
for i in range(4):
cchar: uchar = uchar(data[i])
if HQX_VALS[cchar] < 0:
return None, crc
# 解码
# Unpack 4 HQX characters back into 3 bytes.
m: uint = 0
for i in range(4):
m = (m << 6) | uint(HQX_VALS[uchar(data[i])])
@@ -330,7 +329,7 @@ def a2b_hqx(data: str, crc: ushort = 0) -> tuple[str, ushort]:
out[1] = char((m >> 8) & 0xFF)
out[2] = char(m & 0xFF)
out[3] = '\0'
# CRC 初始化 + 正确计算
# Seed the CRC with the caller's value, then fold in the 3 decoded bytes.
s: ushort = crc
for i in range(3):
byte: uchar = uchar(out[i])
+11 -14
View File
@@ -4,49 +4,46 @@ import stdlib
novirtual class Coroutine:
"""协程帧句柄(M1:__ptr__ 持有 create_<name>() 返回的帧指针)。
纯句柄语义:从不实例化/多态分派,编译器全拦截 coro.* 调用;
用户代码句柄统一用 t.CPtr。"""
"""Coroutine frame handle (M1: __ptr__ holds the frame pointer returned by create_<name>()).
Pure handle semantics: never instantiated or polymorphically dispatched, the compiler intercepts all coro.* calls; user code uses t.CPtr as the handle."""
__ptr__: rawptr
def coro_resume(c: Coroutine) -> int:
# M1 占位:实际由编译器将 coro.coro_run 内联翻译为 resume_<name> 循环,
# 本函数不会被真正调用(仅保证模块可编译)。
# M1 placeholder: the compiler inlines coro.coro_run into a resume_<name> loop; this function is never actually called (it only keeps the module compilable).
return 0
def coro_result(c: Coroutine) -> int:
# M1 占位:coro_run 内联翻译直接从帧 ret 槽取结果。
# M1 placeholder: coro_run's inline translation reads the result directly from the frame ret slot.
return 0
def coro_run(c: Coroutine) -> int:
# M1 占位实现:编译器在调用点对 coro.coro_run 做内联翻译(resume 循环)。
# M1 placeholder: the compiler inlines coro.coro_run at the call site (resume loop).
s: int = 0
while s == 0:
s = coro_resume(c)
return coro_result(c)
def coro_sleep(ms: int) -> int:
# M3 占位:编译器将 await coro_sleep(ms) 内联翻译为帧 wake 槽挂起,
# 本函数不会被真正调用(仅保证模块可编译)。
# M3 placeholder: the compiler inlines await coro_sleep(ms) into a frame wake-slot suspend; this function is never actually called (it only keeps the module compilable).
return 0
def coro_destroy(c: Coroutine):
# M3 占位:编译器将 coro.coro_destroy(帧) 内联翻译为 free。
# M3 placeholder: the compiler inlines coro.coro_destroy(frame) into free.
return
def coro_exception(c: Coroutine) -> int:
# M4 占位:编译器将 coro.coro_exception(帧) 内联翻译为帧 exc 槽读取。
# M4 placeholder: the compiler inlines coro.coro_exception(frame) into a frame exc-slot read.
return 0
def coro_gather(a: Coroutine) -> int:
# M4 占位:编译器将 coro.coro_gather(帧...) 内联翻译为并发推进循环。
# M4 placeholder: the compiler inlines coro.coro_gather(frames...) into a concurrent-advance loop.
return 0
def coro_exception_msg(c: Coroutine) -> t.CPtr:
# M7 占位:编译器将 coro.coro_exception_msg(帧) 内联翻译为帧 msg 槽读取。
# 注意:TPV(Viper)不支持裸 null,用 t.CPtr(0) 构造空指针(两编译器兼容)。
# M7 placeholder: the compiler inlines coro.coro_exception_msg(frame) into a frame msg-slot read.
# Note: TPV (Viper) does not support bare null; use t.CPtr(0) to build a null pointer (compatible with both compilers).
return None
+120 -122
View File
@@ -54,10 +54,10 @@ from fltk._cfl_theme import *
# ============================================================
# Fl — FLTK 全局静态类(事件循环 / 事件 / 颜色)
# Fl — FLTK global static class (event loop / events / colors)
# ============================================================
novirtual class Fl:
"""FLTK 全局函数集合(静态方法)"""
"""FLTK global function collection (static methods)."""
@staticmethod
def run() -> int:
@@ -129,11 +129,10 @@ novirtual class Fl:
@staticmethod
def set_scheme(scheme: str) -> int:
"""切换全局风格 scheme。
"""Switch the global style scheme.
内置风格: 'base' / 'plastic' / 'gtk+' / 'gleam' / 'oxy'。
调用后 FLTK 自动重载并重绘所有窗口。
用法: fltk.Fl.set_scheme("plastic")"""
Built-in: 'base' / 'plastic' / 'gtk+' / 'gleam' / 'oxy'; FLTK reloads and
redraws all windows afterwards. Usage: fltk.Fl.set_scheme("plastic")"""
return Fl_set_scheme(scheme)
@staticmethod
@@ -182,77 +181,77 @@ novirtual class Fl:
@staticmethod
def scheme_string() -> str:
"""当前风格名('base'/'plastic'/'gtk+'/'gleam'/'oxy')。"""
"""Current scheme name ('base'/'plastic'/'gtk+'/'gleam'/'oxy')."""
return Fl_scheme_string()
@staticmethod
def reload_scheme() -> int:
"""重载当前风格(修改背景/前景色后调用生效)。"""
"""Reload the current scheme (call after changing background/foreground colors)."""
return Fl_reload_scheme()
@staticmethod
def set_background(r: int, g: int, b: int):
"""设置全局背景色(0~255),之后需 Fl.reload_scheme()。"""
"""Set the global background color (0~255); call Fl.reload_scheme() afterwards."""
Fl_background(r, g, b)
@staticmethod
def set_background2(r: int, g: int, b: int):
"""设置全局次级背景色(0~255)。"""
"""Set the global secondary background color (0~255)."""
Fl_background2(r, g, b)
@staticmethod
def set_foreground(r: int, g: int, b: int):
"""设置全局前景色(文字,0~255)。"""
"""Set the global foreground (text) color (0~255)."""
Fl_foreground(r, g, b)
# ----- Fl_Theme 主题(C++ 主题库,libfltk_theme.a) -----
# ----- Fl_Theme theme (C++ theme library, libfltk_theme.a) -----
@staticmethod
def set_theme_dark():
"""应用 Fl_Theme 深色主题(flat 控件 + BLACK 色板 + 深灰背景/白前景)。"""
"""Apply the Fl_Theme dark theme (flat widgets + BLACK palette + dark-gray bg / white fg)."""
Fl_Theme_apply_dark()
@staticmethod
def set_theme_metro():
"""应用 Fl_Theme Metro 主题(metro 控件 + 蓝灰配色)。"""
"""Apply the Fl_Theme Metro theme (metro widgets + blue-gray palette)."""
Fl_Theme_apply_metro()
@staticmethod
def set_border_radius(r: int):
"""设置 flat 控件圆角半径(默认 5;需在应用主题前调用)。"""
"""Set the flat widget corner radius (default 5; call before applying a theme)."""
Fl_Theme_set_border_radius(r)
@staticmethod
def apply_flat_scheme():
"""仅应用 flat 控件绘制方案(不改颜色)。"""
"""Apply only the flat widget drawing scheme (colors unchanged)."""
Fl_Theme_apply_flat_scheme()
@staticmethod
def apply_metro_scheme():
"""仅应用 metro 控件绘制方案(不改颜色)。"""
"""Apply only the metro widget drawing scheme (colors unchanged)."""
Fl_Theme_apply_metro_scheme()
# ----- 定时器 / 线程唤醒 -----
# ----- Timers / thread wakeup -----
@staticmethod
def add_timeout(seconds: double, cb: bytes, data: bytes = 0):
"""注册一次性定时回调(seconds 秒后触发)。回调签名: def cb(w: bytes, data: bytes)"""
"""Register a one-shot timer callback (fires after seconds). cb(w: bytes, data: bytes)."""
Fl_add_timeout(seconds, cb, data)
@staticmethod
def repeat_timeout(seconds: double, cb: bytes, data: bytes = 0):
"""注册重复定时回调(每 seconds 秒触发,回调内需再调 repeat_timeout 保持)。"""
"""Register a repeating timer callback (every seconds; call repeat_timeout again inside to keep it)."""
Fl_repeat_timeout(seconds, cb, data)
@staticmethod
def awake():
"""唤醒另一线程的 Fl.run/Fl.wait 事件循环。"""
"""Wake up the Fl.run/Fl.wait event loop in another thread."""
Fl_awake()
@staticmethod
def awake_callback(handler: bytes, data: bytes = 0) -> int:
"""注册跨线程唤醒回调(另一线程 Fl.awake() 后主循环调用 handler)。"""
"""Register a cross-thread wakeup callback (the main loop calls handler after another thread's Fl.awake())."""
return Fl_awake_callback(handler, data)
# ----- 补全事件查询 -----
# ----- Additional event queries -----
@staticmethod
def event_original_key() -> int:
return Fl_event_original_key()
@@ -287,10 +286,10 @@ novirtual class Fl:
# ============================================================
# Fl_Widget — 控件基类包装
# Fl_Widget — widget base class wrapper
# ============================================================
novirtual class Fl_Widget:
"""FLTK 控件基类(不透明指针包装)"""
"""FLTK widget base class (opaque pointer wrapper)."""
_ptr: Fl_WidgetPtr
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
@@ -371,36 +370,36 @@ novirtual class Fl_Widget:
def delete(self):
Fl_Widget_delete(self._ptr)
# ----- 回调 -----
# ----- Callbacks -----
def set_callback(self, cb: bytes, data: bytes = 0):
"""注册控件回调(如点击/变更触发)。cb 传 Viper 模块级函数名,自动转函数指针。
回调签名: def cb(w: bytes, data: bytes): ...(w=控件指针,可转回本类指针用)。
触发时机由 set_when 控制(默认 Fl_WHEN_RELEASE)。"""
"""Register a widget callback (e.g. click/change). cb is a Viper module-level
function name auto-cast to a function pointer: cb(w: bytes, data: bytes).
Trigger time is controlled by set_when (default Fl_WHEN_RELEASE)."""
Fl_Widget_set_callback(self._ptr, cb, data)
def callback(self) -> bytes:
return Fl_Widget_callback(self._ptr)
def do_callback(self):
"""手动触发本控件回调(不经过事件循环)。"""
"""Manually invoke this widget's callback (bypassing the event loop)."""
Fl_Widget_do_callback(self._ptr)
def set_when(self, when: int):
"""设置回调触发条件(Fl_WHEN_* 常量见 _enums.vp)。"""
"""Set the callback trigger condition (Fl_WHEN_* constants in _enums.vp)."""
Fl_Widget_set_when(self._ptr, when)
def when(self) -> int:
return Fl_Widget_when(self._ptr)
def set_user_data(self, data: bytes):
"""设置回调用户数据(回调的第二个参数 data)。"""
"""Set the callback user data (the callback's second argument data)."""
Fl_Widget_set_user_data(self._ptr, data)
def user_data(self) -> bytes:
return Fl_Widget_user_data(self._ptr)
def set_image(self, img: bytes):
"""设置控件图标(传 Fl_PNG_Image 等图片对象的 _ptr)。"""
"""Set the widget image (pass the _ptr of an image object such as Fl_PNG_Image)."""
Fl_Widget_set_image(self._ptr, img)
def image(self) -> bytes:
@@ -408,10 +407,10 @@ novirtual class Fl_Widget:
# ============================================================
# Fl_Window — 窗口包装
# Fl_Window — window wrapper
# ============================================================
novirtual class Fl_Window(Fl_Widget):
"""FLTK 窗口"""
"""FLTK window."""
def __init__(self, w: int, h: int, title: str = ""):
self._ptr = Fl_Window_new(0, 0, w, h, title)
@@ -466,20 +465,20 @@ novirtual class Fl_Window(Fl_Widget):
# ============================================================
# Fl_Box — 静态文本/装饰框
# Fl_Box — static text/decorative box
# ============================================================
novirtual class Fl_Box(Fl_Widget):
"""FLTK 文本框/装饰框"""
"""FLTK text/decorative box."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Box_new(x, y, w, h, label)
# ============================================================
# Fl_Button — 按钮包装
# Fl_Button — button wrapper
# ============================================================
novirtual class Fl_Button(Fl_Widget):
"""FLTK 按钮"""
"""FLTK button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Button_new(x, y, w, h, label)
@@ -510,7 +509,7 @@ novirtual class Fl_Button(Fl_Widget):
novirtual class Fl_Check_Button(Fl_Button):
"""FLTK 复选框"""
"""FLTK check button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Check_Button_new(x, y, w, h, label)
@@ -523,7 +522,7 @@ novirtual class Fl_Check_Button(Fl_Button):
novirtual class Fl_Radio_Button(Fl_Button):
"""FLTK 单选按钮"""
"""FLTK radio button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Radio_Button_new(x, y, w, h, label)
@@ -536,21 +535,21 @@ novirtual class Fl_Radio_Button(Fl_Button):
novirtual class Fl_Repeat_Button(Fl_Button):
"""FLTK 重复触发按钮"""
"""FLTK repeat button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Repeat_Button_new(x, y, w, h, label)
novirtual class Fl_Return_Button(Fl_Button):
"""FLTK 回车按钮"""
"""FLTK return button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Return_Button_new(x, y, w, h, label)
novirtual class Fl_Light_Button(Fl_Button):
"""FLTK 指示灯按钮"""
"""FLTK light button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Light_Button_new(x, y, w, h, label)
@@ -563,10 +562,10 @@ novirtual class Fl_Light_Button(Fl_Button):
# ============================================================
# Fl_Input / Fl_Output — 文本输入输出
# Fl_Input / Fl_Output — text input/output
# ============================================================
novirtual class Fl_Input(Fl_Widget):
"""FLTK 单行文本输入框"""
"""FLTK single-line text input."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Input_new(x, y, w, h, label)
@@ -591,7 +590,7 @@ novirtual class Fl_Input(Fl_Widget):
novirtual class Fl_Output(Fl_Widget):
"""FLTK 单行文本输出框"""
"""FLTK single-line text output."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Output_new(x, y, w, h, label)
@@ -614,10 +613,10 @@ novirtual class Fl_Float_Input(Fl_Input):
# ============================================================
# Fl_Text_Buffer / Fl_Text_Display — 多行文本
# Fl_Text_Buffer / Fl_Text_Display — multi-line text
# ============================================================
novirtual class Fl_Text_Buffer:
"""FLTK 文本缓冲"""
"""FLTK text buffer."""
_ptr: Fl_Text_BufferPtr
@@ -653,7 +652,7 @@ novirtual class Fl_Text_Buffer:
novirtual class Fl_Text_Display(Fl_Widget):
"""FLTK 多行文本显示"""
"""FLTK multi-line text display."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Text_Display_new(x, y, w, h, label)
@@ -675,10 +674,10 @@ novirtual class Fl_Text_Display(Fl_Widget):
# ============================================================
# Fl_Group — 容器包装
# Fl_Group — container wrapper
# ============================================================
novirtual class Fl_Group(Fl_Widget):
"""FLTK 控件组"""
"""FLTK widget group."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Group_new(x, y, w, h, label)
@@ -709,7 +708,7 @@ novirtual class Fl_Group(Fl_Widget):
novirtual class Fl_Scroll(Fl_Group):
"""FLTK 滚动容器"""
"""FLTK scroll container."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Scroll_new(x, y, w, h, label)
@@ -731,7 +730,7 @@ novirtual class Fl_Scroll(Fl_Group):
novirtual class Fl_Tabs(Fl_Group):
"""FLTK 选项卡容器"""
"""FLTK tab container."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Tabs_new(x, y, w, h, label)
@@ -744,10 +743,10 @@ novirtual class Fl_Tabs(Fl_Group):
# ============================================================
# Fl_Slider / Fl_Value_Input / Fl_Value_Output — 数值控件
# Fl_Slider / Fl_Value_Input / Fl_Value_Output — numeric widgets
# ============================================================
novirtual class Fl_Slider(Fl_Widget):
"""FLTK 滑动条"""
"""FLTK slider."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Slider_new(x, y, w, h, label)
@@ -781,7 +780,7 @@ novirtual class Fl_Slider(Fl_Widget):
novirtual class Fl_Value_Input(Fl_Widget):
"""FLTK 数值输入框"""
"""FLTK numeric input."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Value_Input_new(x, y, w, h, label)
@@ -797,7 +796,7 @@ novirtual class Fl_Value_Input(Fl_Widget):
novirtual class Fl_Value_Output(Fl_Widget):
"""FLTK 数值输出框"""
"""FLTK numeric output."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Value_Output_new(x, y, w, h, label)
@@ -810,7 +809,7 @@ novirtual class Fl_Value_Output(Fl_Widget):
novirtual class Fl_Progress(Fl_Widget):
"""FLTK 进度条"""
"""FLTK progress bar."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Progress_new(x, y, w, h, label)
@@ -829,10 +828,10 @@ novirtual class Fl_Progress(Fl_Widget):
# ============================================================
# Fl_Choice — 下拉选择
# Fl_Choice — drop-down choice
# ============================================================
novirtual class Fl_Choice(Fl_Widget):
"""FLTK 下拉选择框"""
"""FLTK drop-down choice box."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Choice_new(x, y, w, h, label)
@@ -851,14 +850,14 @@ novirtual class Fl_Choice(Fl_Widget):
# ============================================================
# Fl_Menu_Bar — 菜单栏
# Fl_Menu_Bar — menu bar
# ============================================================
novirtual class Fl_Menu_Bar(Fl_Widget):
"""FLTK 菜单栏
"""FLTK menu bar.
add 走 cfltk 完整 6 参签名(name/shortcut/回调/数据/flags),
cb 可传 Viper 模块级函数名(回调签名: def cb(w: bytes, data: bytes));
不传回调(默认)时菜单项无动作。简单下拉建议用 add_choice。"""
add uses the full cfltk 6-arg signature (name/shortcut/cb/data/flags); cb may be
a Viper module-level function name (cb(w: bytes, data: bytes)). Default: no action.
For simple drop-downs prefer add_choice."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Menu_Bar_new(x, y, w, h, label)
@@ -871,14 +870,13 @@ novirtual class Fl_Menu_Bar(Fl_Widget):
# ============================================================
# Fl_Native_File_Chooser — 系统文件对话框
# Fl_Native_File_Chooser — native file dialog
# ============================================================
novirtual class Fl_Native_File_Chooser:
"""FLTK 原生文件选择对话框
"""FLTK native file dialog.
构造需传对话框类型常量(如 Fl_Native_File_Chooser_BROWSE_FILE /
BROWSE_DIRECTORY / BROWSE_MULTI_FILE / BROWSE_MULTI_DIRECTORY /
BROWSE_SAVE_FILE,见 _enums.vp)。"""
The constructor takes a dialog-type constant (BROWSE_FILE / BROWSE_DIRECTORY /
BROWSE_MULTI_FILE / BROWSE_MULTI_DIRECTORY / BROWSE_SAVE_FILE; see _enums.vp)."""
_ptr: Fl_Native_File_ChooserPtr
def __init__(self, type_: int):
@@ -934,77 +932,77 @@ novirtual class Fl_Native_File_Chooser:
# ============================================================
# 常用别名
# Common aliases
# ============================================================
def fltk_init():
"""初始化 FLTK(多线程环境需先 Fl_lock 再 init_all)。"""
"""Initialize FLTK (in multithreaded environments call Fl_lock before init_all)."""
Fl_init_all()
# ============================================================
# 补充按钮类
# Additional button classes
# ============================================================
novirtual class Fl_Toggle_Button(Fl_Button):
"""FLTK 切换按钮(按下保持)"""
"""FLTK toggle button (stays pressed)."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Toggle_Button_new(x, y, w, h, label)
novirtual class Fl_Round_Button(Fl_Button):
"""FLTK 圆形按钮"""
"""FLTK round button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Round_Button_new(x, y, w, h, label)
novirtual class Fl_Radio_Round_Button(Fl_Button):
"""FLTK 圆形单选按钮"""
"""FLTK round radio button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Radio_Round_Button_new(x, y, w, h, label)
novirtual class Fl_Radio_Light_Button(Fl_Button):
"""FLTK 指示灯单选按钮"""
"""FLTK radio light button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Radio_Light_Button_new(x, y, w, h, label)
novirtual class Fl_Shortcut_Button(Fl_Button):
"""FLTK 快捷键录入按钮"""
"""FLTK shortcut button."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Shortcut_Button_new(x, y, w, h, label)
# ============================================================
# 补充输入类
# Additional input classes
# ============================================================
novirtual class Fl_Multiline_Input(Fl_Input):
"""FLTK 多行文本输入框"""
"""FLTK multi-line text input."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Multiline_Input_new(x, y, w, h, label)
novirtual class Fl_Secret_Input(Fl_Input):
"""FLTK 密码输入框(显示为 *)"""
"""FLTK password input (shown as *)."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Secret_Input_new(x, y, w, h, label)
novirtual class Fl_File_Input(Fl_Input):
"""FLTK 文件路径输入框"""
"""FLTK file-path input."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_File_Input_new(x, y, w, h, label)
novirtual class Fl_Multiline_Output(Fl_Output):
"""FLTK 多行文本输出框"""
"""FLTK multi-line text output."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Multiline_Output_new(x, y, w, h, label)
@@ -1014,14 +1012,14 @@ novirtual class Fl_Multiline_Output(Fl_Output):
# Fl_Nice_Slider / Fl_Spinner / Fl_Clock
# ============================================================
novirtual class Fl_Nice_Slider(Fl_Slider):
"""FLTK 美观滑块"""
"""FLTK nice slider."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Nice_Slider_new(x, y, w, h, label)
novirtual class Fl_Spinner(Fl_Widget):
"""FLTK 数字微调框"""
"""FLTK spinner."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Spinner_new(x, y, w, h, label)
@@ -1052,41 +1050,41 @@ novirtual class Fl_Spinner(Fl_Widget):
novirtual class Fl_Clock(Fl_Widget):
"""FLTK 时钟(模拟表盘)"""
"""FLTK clock (analog dial)."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Clock_new(x, y, w, h, label)
# ============================================================
# 补充容器类
# Additional container classes
# ============================================================
novirtual class Fl_Pack(Fl_Group):
"""FLTK 自动布局容器(子控件按序排列)"""
"""FLTK auto-layout container (children laid out in order)."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Pack_new(x, y, w, h, label)
novirtual class Fl_Tile(Fl_Group):
"""FLTK 可拖动分隔容器"""
"""FLTK resizable-splitter container."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Tile_new(x, y, w, h, label)
novirtual class Fl_Flex(Fl_Group):
"""FLTK 弹性布局容器"""
"""FLTK flex layout container."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Flex_new(x, y, w, h, label)
# ============================================================
# Fl_Menu_Button — 弹出菜单按钮
# Fl_Menu_Button — popup menu button
# ============================================================
novirtual class Fl_Menu_Button(Fl_Widget):
"""FLTK 弹出菜单按钮(右键/长按弹出)"""
"""FLTK popup menu button (shown on right-click / long-press)."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Menu_Button_new(x, y, w, h, label)
@@ -1096,16 +1094,16 @@ novirtual class Fl_Menu_Button(Fl_Widget):
# ============================================================
# Fl_Browser — 列表浏览
# Fl_Browser — list browser
# ============================================================
novirtual class Fl_Browser(Fl_Widget):
"""FLTK 列表浏览框"""
"""FLTK list browser."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Browser_new(x, y, w, h, label)
def value(self) -> int:
"""当前选中行号(1 起;0=未选中)"""
"""Current selected line number (1-based; 0=unselected)."""
return Fl_Browser_value(self._ptr)
def add(self, newtext: str):
@@ -1119,16 +1117,16 @@ novirtual class Fl_Browser(Fl_Widget):
# ============================================================
# Fl_Tree — 树形控件
# Fl_Tree — tree widget
# ============================================================
novirtual class Fl_Tree(Fl_Widget):
"""FLTK 树形控件"""
"""FLTK tree widget."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Tree_new(x, y, w, h, label)
def add(self, name: str) -> Fl_Tree_ItemPtr:
"""添加节点('a/b/c' 路径自动建父子层级)"""
"""Add a node ('a/b/c' path builds parent/child levels automatically)."""
return Fl_Tree_add(self._ptr, name)
def clear(self):
@@ -1139,22 +1137,22 @@ novirtual class Fl_Tree(Fl_Widget):
# Fl_Text_Editor / Fl_Terminal
# ============================================================
novirtual class Fl_Text_Editor(Fl_Text_Display):
"""FLTK 文本编辑器"""
"""FLTK text editor."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Text_Editor_new(x, y, w, h, label)
def insert(self, text: str):
"""在光标处插入文本"""
"""Insert text at the cursor."""
Fl_Text_Editor_insert(self._ptr, text)
def overstrike(self, text: str):
"""在光标处覆盖插入文本"""
"""Overwrite text at the cursor."""
Fl_Text_Editor_overstrike(self._ptr, text)
novirtual class Fl_Terminal(Fl_Widget):
"""FLTK 终端模拟控件(ANSI 转义支持)"""
"""FLTK terminal emulation widget (ANSI escape support)."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Terminal_new(x, y, w, h, label)
@@ -1164,13 +1162,13 @@ novirtual class Fl_Terminal(Fl_Widget):
# ============================================================
# Fl_File_Chooser — 文件选择对话框
# Fl_File_Chooser — file dialog
# ============================================================
novirtual class Fl_File_Chooser:
"""FLTK 文件选择对话框(cfltk 封装,非原生)
"""FLTK file dialog (cfltk wrapper, not native).
new(directory, pattern, type, title);type 用 Fl_File_Chooser_* 常量
(见 _enums.vp:SINGLE/MULTI/DIRECTORY/CREATE/SAVE 等)。"""
new(directory, pattern, type, title); type uses Fl_File_Chooser_* constants
(see _enums.vp: SINGLE/MULTI/DIRECTORY/CREATE/SAVE, etc.)."""
_ptr: Fl_File_ChooserPtr
@@ -1220,7 +1218,7 @@ novirtual class Fl_File_Chooser:
Fl_File_Chooser_hide(self._ptr)
def value(self, f: int) -> str:
"""选中的第 f 个文件名(0 起;多选时用 count 遍历)"""
"""Selected filename at index f (0-based; iterate with count for multi-select)."""
return Fl_File_Chooser_value(self._ptr, f)
def delete(self):
@@ -1228,10 +1226,10 @@ novirtual class Fl_File_Chooser:
# ============================================================
# Fl_Help_Dialog — HTML 帮助对话框
# Fl_Help_Dialog — HTML help dialog
# ============================================================
novirtual class Fl_Help_Dialog:
"""FLTK HTML 帮助对话框"""
"""FLTK HTML help dialog."""
_ptr: Fl_Help_DialogPtr
@@ -1239,11 +1237,11 @@ novirtual class Fl_Help_Dialog:
self._ptr = Fl_Help_Dialog_new()
def load(self, f: str) -> int:
"""加载 HTML 文件"""
"""Load an HTML file."""
return Fl_Help_Dialog_load(self._ptr, f)
def set_value(self, f: str):
"""直接设置 HTML 内容"""
"""Set HTML content directly."""
Fl_Help_Dialog_set_value(self._ptr, f)
def value(self) -> str:
@@ -1266,10 +1264,10 @@ novirtual class Fl_Help_Dialog:
# ============================================================
# Fl_Input_Choice — 输入+下拉组合
# Fl_Input_Choice — input + drop-down combo
# ============================================================
novirtual class Fl_Input_Choice(Fl_Group):
"""FLTK 文本输入 + 下拉选择组合控件"""
"""FLTK text input + drop-down choice combo widget."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Input_Choice_new(x, y, w, h, label)
@@ -1278,21 +1276,21 @@ novirtual class Fl_Input_Choice(Fl_Group):
Fl_Input_Choice_add(self._ptr, s)
def value(self) -> str:
"""当前输入框文本"""
"""Current input box text."""
return Fl_Input_Choice_value(self._ptr)
# ============================================================
# Fl_Color_Chooser — 颜色选择
# Fl_Color_Chooser — color chooser
# ============================================================
novirtual class Fl_Color_Chooser(Fl_Group):
"""FLTK 颜色选择器"""
"""FLTK color chooser."""
def __init__(self, x: int, y: int, w: int, h: int, label: str = ""):
self._ptr = Fl_Color_Chooser_new(x, y, w, h, label)
def set_rgb(self, r: double, g: double, b: double) -> int:
"""设置 RGB 值(0.0~1.0),返回 1=值变更"""
"""Set RGB values (0.0~1.0); returns 1 if the value changed."""
return Fl_Color_Chooser_set_rgb(self._ptr, r, g, b)
def show(self):
@@ -1300,10 +1298,10 @@ novirtual class Fl_Color_Chooser(Fl_Group):
# ============================================================
# 图片类
# Image classes
# ============================================================
novirtual class Fl_PNG_Image:
"""FLTK PNG 图片(从文件加载)"""
"""FLTK PNG image (loaded from a file)."""
_ptr: Fl_PNG_ImagePtr
@@ -1312,7 +1310,7 @@ novirtual class Fl_PNG_Image:
novirtual class Fl_JPEG_Image:
"""FLTK JPEG 图片"""
"""FLTK JPEG image."""
_ptr: Fl_JPEG_ImagePtr
@@ -1321,7 +1319,7 @@ novirtual class Fl_JPEG_Image:
novirtual class Fl_GIF_Image:
"""FLTK GIF 图片"""
"""FLTK GIF image."""
_ptr: Fl_GIF_ImagePtr
+4 -4
View File
@@ -7,15 +7,15 @@
# ============================================================
import t, c
# 应用深色主题(flat 控件 + BLACK 色板)
# Apply the dark theme (flat widgets + BLACK palette)
extern def Fl_Theme_apply_dark()
# 应用 Metro 主题(metro 控件 + 蓝灰配色)
# Apply the Metro theme (metro widgets + blue-gray palette)
extern def Fl_Theme_apply_metro()
# 设置 flat 控件圆角半径(默认 5;应用主题前调用)
# Set the flat widget corner radius (default 5; call before applying a theme)
extern def Fl_Theme_set_border_radius(radius: int)
# 仅应用 flat / metro 控件绘制方案(不改颜色)
# Apply only the flat / metro widget drawing schemes (colors unchanged)
extern def Fl_Theme_apply_flat_scheme()
extern def Fl_Theme_apply_metro_scheme()
+22 -22
View File
@@ -25,13 +25,13 @@ from stdint import *
# GzipFile
# ============================================================
class GzipFile:
ok: int # 1 = 可正常使用
err: int # 错误码(0 无, <0 出错)
src: bytes # 整个 .gz 文件的内存副本(构造时读入, close 释放)
ok: int # 1 = usable
err: int # error code (0 = none, <0 = error)
src: bytes # in-memory copy of whole .gz file (read in ctor, freed by close)
src_len: sizet
dst: bytes # 解压输出缓冲(read_all 生成, close 释放)
dst_len: sizet # 解压结果长度
_need_close_src: int # 是否持有 src(读失败时为 0)
dst: bytes # decompressed output buffer (created by read_all, freed by close)
dst_len: sizet # decompressed length
_need_close_src: int # whether src is owned (0 if read failed)
def __init__(self, path: str):
self.ok = 0
@@ -46,15 +46,15 @@ class GzipFile:
return
f: fileio.File = fileio.File(path, fileio.MODE.R)
if f.closed:
self.err = -2 # 打不开文件
self.err = -2 # cannot open file
return
sz: longlong = f.size()
if sz < 18:
f.close()
self.err = -3 # 文件过小
self.err = -3 # file too small
return
self.src_len = sizet(sz)
# read_all 契约: 最多读 max_count-1 字节并补 NUL; 故按 src_len+1 申请与请求
# read_all contract: reads up to max_count-1 bytes and NUL-terminates; request src_len+1.
buf: bytes = stdlib.malloc(self.src_len + 1)
if buf is None:
f.close()
@@ -64,9 +64,9 @@ class GzipFile:
f.close()
if got <= 0 or sizet(got) != self.src_len:
stdlib.free(buf)
self.err = -5 # 读取失败或被截断
self.err = -5 # read failed or truncated
return
# gzip 魔数校验
# gzip magic-number check
ub: uchar* = buf
if ub[0] != 0x1F or ub[1] != 0x8B or ub[2] != 8:
stdlib.free(buf)
@@ -77,18 +77,18 @@ class GzipFile:
self.ok = 1
def read_all(self) -> bytes:
"""解压整个成员。返回内部持有的输出缓冲(或 None); 长度见 self.dst_len。"""
"""Decompress the whole member. Returns internal output buffer (or None); length in self.dst_len."""
if self.ok == 0 or self.src is None:
return None
if self.dst is not None:
return self.dst # 已解压过
return self.dst # already decompressed
want: uint32 = gzip_isize(self.src, self.src_len)
cap: sizet = sizet(want)
if cap == 0:
cap = self.src_len * 64 # 无 ISIZE 时保守初值
cap = self.src_len * 64 # conservative initial value when no ISIZE
if cap < 1024:
cap = 1024
# 保险起见: ISIZE 与实际不符时报"目标缓冲不足", 就翻倍重试(正常情况下一次成功)
# If ISIZE mismatches actual size (buffer-too-small), double cap and retry (normally one pass).
out: BYTEPTR = None
n: LONG = 0
while True:
@@ -108,7 +108,7 @@ class GzipFile:
break
if n <= 0:
stdlib.free(out)
self.err = int(n) # n 为负错误码, 原样保存
self.err = int(n) # n is a negative error code; store as-is
return None
self.dst = out
self.dst_len = sizet(n)
@@ -127,7 +127,7 @@ class GzipFile:
self.ok = 0
# ============================================================
# 位流状态(模块级, 单线程; 每次 _gzip_decode 重置)
# Bit-stream state (module-level, single-thread; reset per _gzip_decode)
# ============================================================
_g_bits: uint64 = 0
_g_bitcnt: int = 0
@@ -137,12 +137,12 @@ _g_src_len: sizet = 0
def gzip_isize(src: t.CPtr, src_len: sizet) -> uint32:
"""gzip 尾部 ISIZE(小端 u32), 即原始长度提示"""
"""gzip trailing ISIZE (little-endian u32): original length hint."""
if src is None or src_len < 4:
return 0
ub: uchar* = src
p: sizet = src_len - 4
# 小端 u32 拼装(64 位中间量后收窄)
# Assemble little-endian u32 (build in 64-bit then narrow)
v64: uint64 = uint64(ub[p]) \
+ uint64(ub[p + 1]) * 256 \
+ uint64(ub[p + 2]) * 65536 \
@@ -313,7 +313,7 @@ def _cl_order_set(arr: int*, idx: int, val: int):
# ============================================================
# 核心解压(模块级私有)
# Core decompression (module-private)
# ============================================================
def _gzip_decode(src: t.CPtr, src_len: sizet,
dst: t.CPtr, dst_cap: sizet) -> LONG:
@@ -371,7 +371,7 @@ def _gzip_decode(src: t.CPtr, src_len: sizet,
or cl_sorted is None or pos_tmp is None:
return -4
# 全部表缓冲清零(初始 malloc 未清零; 防个别路径读到垃圾表导致提前/错乱收尾)
# Zero all table buffers (malloc is uninitialized; avoids garbage tables causing early/bad finish).
string.memset(lit_len, 0, 288 * 4)
string.memset(lit_cnt, 0, 17 * 4)
string.memset(lit_first, 0, 17 * 4)
@@ -606,5 +606,5 @@ def _gzip_decode(src: t.CPtr, src_len: sizet,
stdlib.free(pos_tmp)
if err != 0:
return LONG(err) # err 本身为负错误码, 不能再取负(否则被上层当成"长度>0 成功")
return LONG(err) # err is already a negative code; do not negate it (else callers treat >0 as success)
return LONG(wpos)
+5 -7
View File
@@ -45,7 +45,7 @@ class HashTable:
ht: HashTable = HashTable(mb)
ht.set_int("x", 42)
p: t.CPtr = ht["x"]
v: int = c.Deref((int | t.CPtr)(p)) # int 值按 i32 存,读回须给 int* 视图
v: int = c.Deref((int | t.CPtr)(p)) # int value stored as i32; read it back with an int* view
"""
__slots__: t.CPtr # Base address of slot array
__count__: sizet = 0 # Number of occupied entries
@@ -79,10 +79,8 @@ class HashTable:
def _slot_value(self, idx: sizet) -> t.CPtr:
"""Read value pointer from target slot."""
# value 槽存的是指针,注解须为「指针的指针」:X|t.CPtr 的合并规则是 Ptr(resolve(X)),
# 故 t.CUInt64T|t.CPtr|t.CPtr = Ptr(Ptr(i64)) = i64**,取下标得 i64*(指针),
# 与返回类型 t.CPtr(i8*)同级指针可隐式 bitcast。若只写 t.CUInt64T|t.CPtr
# (= i64*,存哈希整数用),取下标得标量 i64 → 撞「整数隐式转指针禁止」。
# The value slot holds a pointer, so the annotation must be "pointer to pointer": X|t.CPtr merges as Ptr(resolve(X)), so t.CUInt64T|t.CPtr|t.CPtr = Ptr(Ptr(i64)) = i64**, and subscripting yields i64* (a pointer).
# Writing only t.CUInt64T|t.CPtr (= i64*, for hash integers) would make subscripting yield a scalar i64, hitting "implicit int-to-pointer conversion forbidden".
ptr: uint64** = (uint64**)(uint64(self.__slots__) + idx * _HT_SLOT_SIZE + 16)
return ptr[0]
@@ -92,7 +90,7 @@ class HashTable:
h_ptr[0] = h
k_ptr: str* = (str*)(uint64(self.__slots__) + idx * _HT_SLOT_SIZE + 8)
k_ptr[0] = key
# 同 _slot_value:value 槽按「指针的指针」(i64**) 注解,元素才是指针
# Same as _slot_value: the value slot is annotated as "pointer to pointer" (i64**) so the element is a pointer
v_ptr: uint64** = (uint64**)(uint64(self.__slots__) + idx * _HT_SLOT_SIZE + 16)
v_ptr[0] = val
@@ -140,7 +138,7 @@ class HashTable:
h: uint64 = old_h_ptr[0]
if h != _HT_EMPTY and h != _HT_TOMBSTONE:
old_k_ptr: str* = (str*)(uint64(old_slots) + i * _HT_SLOT_SIZE + 8)
# 同 _slot_value:value 槽按「指针的指针」(i64**) 注解,元素才是指针
# Same as _slot_value: the value slot is annotated as "pointer to pointer" (i64**) so the element is a pointer
old_v_ptr: uint64** = (uint64**)(uint64(old_slots) + i * _HT_SLOT_SIZE + 16)
k: str = old_k_ptr[0]
v: t.CPtr = old_v_ptr[0]
+12 -12
View File
@@ -17,8 +17,8 @@ define JSON_OBJECT = 6
# ============================================================
# JsonValue - JSON 值节点
# 使用 mpool 分配:JsonValue(pool) 从池中分配内存
# JsonValue - JSON value node.
# Allocated from mpool: JsonValue(pool) allocates from the pool.
# ============================================================
class JsonValue:
vtype: int
@@ -32,7 +32,7 @@ class JsonValue:
child_count: sizet
def __new__(self, pool: rawptr[memhub.MemManager]):
"""从 mpool 分配 JsonValue 结构体内存并零初始化"""
"""Allocate and zero-initialize a JsonValue struct from mpool."""
ptr: JsonValue* = pool.alloc(JsonValue.__sizeof__())
if ptr:
string.memset(ptr, 0, JsonValue.__sizeof__())
@@ -78,7 +78,7 @@ class JsonValue:
return self.child_count
def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr:
"""Python风格取值:v["key"] 取对象字段"""
"""Python-style get: v["key"] returns an object field."""
if self.vtype != JSON_OBJECT:
return None
if key == None:
@@ -92,7 +92,7 @@ class JsonValue:
return None
def __setitem__(self, key: str, val: 'JsonValue' | t.CPtr):
"""Python风格赋值:v["key"] = val"""
"""Python-style set: v["key"] = val."""
if self.vtype != JSON_OBJECT or key == None or val == None:
return
cur: JsonValue* = self.child
@@ -108,7 +108,7 @@ class JsonValue:
cur.child_count = val.child_count
return
cur = cur.next
# key 不存在,追加新子节点
# key absent: append a new child node
klen: sizet = string.strlen(key)
kbuf: str = self._pool_alloc(klen + 1)
if kbuf != None:
@@ -117,7 +117,7 @@ class JsonValue:
self._append_child(val)
def get_item(self, index: sizet) -> 'JsonValue' | t.CPtr:
"""按索引取数组元素:v.get_item(0)"""
"""Get an array element by index: v.get_item(0)."""
if self.vtype != JSON_ARRAY and self.vtype != JSON_OBJECT:
return None
cur: JsonValue* = self.child
@@ -130,7 +130,7 @@ class JsonValue:
return None
def _pool_alloc(self, size: sizet) -> str:
"""内部方法:从当前对象的 mpool 分配内存(预留,暂返回 None)"""
"""Internal: allocate from current object's mpool (reserved; currently returns None)."""
return None
def _append_child(self, child: 'JsonValue' | t.CPtr):
@@ -145,7 +145,7 @@ class JsonValue:
# ============================================================
# 构造函数:创建各类 JsonValue(从 mpool 分配)
# Constructors: create each kind of JsonValue (allocated from mpool)
# ============================================================
def null(pool: rawptr[memhub.MemManager]) -> JsonValue*:
@@ -199,13 +199,13 @@ def object(pool: rawptr[memhub.MemManager]) -> JsonValue*:
v.vtype = JSON_OBJECT
return v
# 向数组追加元素
# Append an element to an array
def array_append(pool: rawptr[memhub.MemManager], arr: JsonValue*, item: JsonValue*):
if arr == None or item == None: return
if arr.vtype != JSON_ARRAY: return
arr._append_child(item)
# 向对象设置键值对
# Set a key/value pair on an object
def object_set(pool: rawptr[memhub.MemManager], obj: JsonValue*, key: str, val: JsonValue*):
if obj == None or key == None or val == None: return
if obj.vtype != JSON_OBJECT: return
@@ -231,7 +231,7 @@ def object_set(pool: rawptr[memhub.MemManager], obj: JsonValue*, key: str, val:
# ============================================================
# 导入子模块
# Import submodules
# ============================================================
from .__parser import parse
from .__writer import write
+6 -7
View File
@@ -206,9 +206,8 @@ def _parse_array(s: rawptr[ParseState]) -> rawptr[JsonValue]:
return null(s.pool)
arr: rawptr[JsonValue] = array(s.pool)
# 池耗尽 ⇒ array() 返回 None(JsonValue.__new__/null 的既有约定:分配失败返回 None)。
# 若继续走到 arr._append_child(...) 就是空指针解引用 ⇒ 这里向上返回 None
# (与 json.parse 可能返回 None 的约定一致,调用方本就必须判 None)。
# Pool exhausted => array() returns None; continuing to arr._append_child(...) would deref
# a null pointer, so return None here (json.parse may return None; callers must check).
if arr == None:
return null(s.pool)
@@ -218,8 +217,8 @@ def _parse_array(s: rawptr[ParseState]) -> rawptr[JsonValue]:
return arr
item: rawptr[JsonValue] = _parse_value(s)
# item == None ⟺ _parse_value 内部某处分配失败(_parse_value 对语法错误返回的是
# 合法的 JSON_NULL 节点,只有池耗尽才是 None)⇒ 向上返回 None,不追加半条。
# item == None iff allocation failed somewhere inside _parse_value (syntax errors yield
# a valid JSON_NULL node; only pool exhaustion yields None) => return None, append nothing.
if item == None:
return null(s.pool)
arr._append_child(item)
@@ -245,7 +244,7 @@ def _parse_object(s: rawptr[ParseState]) -> rawptr[JsonValue]:
return null(s.pool)
obj: rawptr[JsonValue] = object(s.pool)
# 同 _parse_array:池耗尽时 obj 为 None,不能对 None 调 _append_child。
# Same as _parse_array: obj is None on pool exhaustion; do not call _append_child on None.
if obj == None:
return null(s.pool)
@@ -290,7 +289,7 @@ def _parse_object(s: rawptr[ParseState]) -> rawptr[JsonValue]:
def parse(pool: rawptr[memhub.MemManager], src: str) -> rawptr[JsonValue]:
"""解析 JSON 字符串,返回 JsonValue 树"""
"""Parse a JSON string, returning a JsonValue tree."""
if src == None:
return null(pool)
s: rawptr[ParseState] = ParseState(src, pool)
+17 -18
View File
@@ -21,25 +21,25 @@ from . import (
# cap 的合法区间是 [WRITE_CAP_MIN, WRITE_CAP_MAX]:越界一律返回 None,
# 非法容量不进入 pool.alloc(不会按可疑值去申请内存)。
# ============================================================
define WRITE_CAP_MIN = 64# 下限:最短 JSON 文档(如 {"id":""})含结尾 NUL 也在 64 字节内
define WRITE_CAP_MAX = 67108864# 上限:64 MiB,不小于调用方可能申请的最大输出缓冲
define WRITE_CAP_DEFAULT = 4096# write 三参入口的固定容量(与旧实现 pool.alloc(4096) 一致)
define WRITE_CAP_MIN = 64# lower bound: shortest JSON doc (e.g. {"id":""}) incl NUL fits in 64 bytes
define WRITE_CAP_MAX = 67108864# upper bound: 64 MiB, not less than the largest output buffer a caller may request
define WRITE_CAP_DEFAULT = 4096# fixed capacity for the 3-arg write entry
def _write_char(buf: str, pos: sizet, cap: sizet, ch: char) -> sizet:
if pos + 1 >= cap:
if pos >= cap:
return pos # 已带溢出标志:原样返回,不再写
return cap # 留不出结尾 NUL 位 ⇒ 置溢出标志
return pos # overflow flag already set: return as-is, do not write
return cap # cannot reserve trailing NUL slot => set overflow flag
buf[pos] = ch
return pos + 1
def _write_str(buf: str, pos: sizet, cap: sizet, s: str) -> sizet:
if s == None: return pos
if pos >= cap:
return pos # 已带溢出标志:不再写
return pos # overflow flag already set: stop writing
slen: sizet = string.strlen(s)
if pos + slen >= cap:
return cap # 留不出结尾 NUL 位 ⇒ 置溢出标志
return cap # cannot reserve trailing NUL slot => set overflow flag
string.memcpy(char(uint64(buf) + pos, t.CPtr), s, slen)
return pos + slen
@@ -49,12 +49,12 @@ def _write_string_escaped(buf: str, pos: sizet, cap: sizet, s: str) -> sizet:
return pos
pos = _write_char(buf, pos, cap, '"')
if pos >= cap:
return pos # 已溢出:不再写
return pos # already overflowed: stop writing
i: sizet = 0
slen: sizet = string.strlen(s)
while i < slen:
if pos >= cap:
return pos # 已溢出:提前收敛,不做无意义的逐字符空转
return pos # already overflowed: converge early, skip pointless per-char looping
ch: char = s[i]
if ch == '"':
pos = _write_str(buf, pos, cap, "\\\"")
@@ -76,7 +76,7 @@ def _write_string_escaped(buf: str, pos: sizet, cap: sizet, s: str) -> sizet:
def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue],
pool: rawptr[memhub.MemManager]) -> sizet:
if pos >= cap:
return pos # 已溢出:不再写(递归在此收敛)
return pos # already overflowed: stop writing (recursion converges here)
if val == None:
pos = _write_str(buf, pos, cap, "null")
return pos
@@ -114,7 +114,7 @@ def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue],
first: bool = True
while cur != None:
if pos >= cap:
return pos # 已溢出:停止遍历,不再写
return pos # already overflowed: stop iterating, do not write
if not first:
pos = _write_char(buf, pos, cap, ',')
pos = _write_value(buf, pos, cap, cur, pool)
@@ -131,7 +131,7 @@ def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue],
first2: bool = True
while cur2 != None:
if pos >= cap:
return pos # 已溢出:停止遍历,不再写
return pos # already overflowed: stop iterating, do not write
if not first2:
pos = _write_char(buf, pos, cap, ',')
pos = _write_string_escaped(buf, pos, cap, cur2.key)
@@ -144,20 +144,19 @@ def _write_value(buf: str, pos: sizet, cap: sizet, val: rawptr[JsonValue],
return pos
# 序列化为 JSON 字符串。cap = 输出缓冲容量(字节,含结尾 NUL),缺省 4096
# (与旧实现 pool.alloc(4096) 一致 ⇒ 既有 3 参调用点行为不变)。
# 内容放不下时返回 None(旧实现无边界判断,会写穿池块)。
# Serialize to a JSON string. cap = output buffer size (bytes, incl trailing NUL), default 4096.
# Returns None when content does not fit (never writes past the buffer).
def write(pool: rawptr[memhub.MemManager], val: rawptr[JsonValue], pretty: bool,
cap: sizet = 4096) -> str:
"""将 JsonValue 序列化为 JSON 字符串"""
"""Serialize a JsonValue into a JSON string."""
if cap < 2:
return None # 连结尾 NUL 都放不下
return None # not enough room even for the trailing NUL
buf: str = pool.alloc(cap)
if buf == None:
return None
end_pos: sizet = _write_value(buf, 0, cap, val, pool)
if end_pos >= cap:
return None # 溢出标志:内容超出容量
return None # overflow flag: content exceeds capacity
buf[end_pos] = '\0'
return buf
+28 -42
View File
@@ -220,8 +220,8 @@ def _alloc_site_current() -> uint64:
return _alloc_pending_site
# free 被拒(_is_valid_ptr 判无效)只报一次:这类"静默 no-op"会让块永不回收,
# 历史上正是它把 1 GiB 主池漏满且毫无线索,故首次出现必须留下现场。
# A rejected free (_is_valid_ptr invalid) is reported only once: this silent no-op leaks the block
# forever, so the first occurrence must leave evidence.
_rej_free_reported: int = 0
@@ -592,9 +592,9 @@ class MemBuddy(MemManager):
chk_depth: sizet # current record depth
chk_base: rawptr # record-stack base (stdlib.malloc, separate from the buddy area)
big_alloc_count: sizet # count of allocations >= 8KB (OOM diagnostics)
# mark() 时刻的空闲字节(OOM 诊断):据此区分「本窗口把池吃满」与「进窗口前就已满」
# Free bytes at mark() time (OOM diagnostics): distinguishes "this window filled the pool" from already-full.
mark_free_bytes: sizet = 0
# mark() 时刻的记录深度:OOM 分段诊断用(前缀 vs 本窗口)
# Record depth at mark() time: for OOM segmented diagnostics (prefix vs this window).
mark_depth: sizet = 0
# [DET] Allocation detection (off by default; enable to hunt overflows / live-block overwrites):
# detection=1 -> 2-byte 0xCDCD markers + record table + a probe after each alloc
@@ -724,8 +724,8 @@ class MemBuddy(MemManager):
self.chk_stack = (uint64*)(base)
self.chk_cap = cap
self.chk_depth = 0
# OOM 诊断:记下打点时刻的空闲字节与记录深度,失败报告里据此区分
# 「本窗口把池吃满」与「进窗口前池就已满(历史丢失/常驻占用)」。
# OOM diagnostics: record free bytes and record depth at this mark, so a failure report can
# distinguish "this window filled the pool" from already-full-before-window.
self.mark_free_bytes = self.free_bytes()
self.mark_depth = self.chk_depth
return self.chk_depth
@@ -1410,8 +1410,8 @@ class MemBuddy(MemManager):
self._lock()
result: rawptr = None
if size != 0 and self.usable is not None:
# OOM 诊断:≥8KB 的请求计数。此字段此前从不自增,报告里恒为 big=0,
# 无法据此分辨“多次小块”与“少量大块”。阈值与字段注释口径一致。
# OOM diagnostics: count requests >= 8KB so the report can tell "many small blocks" from
# "few large blocks"; threshold matches the field comment.
if size >= 8192:
self.big_alloc_count += 1
# +CANARY: reserve 8 sentinel bytes past the user area for overflow detection.
@@ -1448,10 +1448,8 @@ class MemBuddy(MemManager):
# it with stdlib.realloc (a full stack would stop recording -> reset misses
# blocks and _is_valid_ptr's stack check degrades).
#
# 记录栈懒建(首次 mark() 之前也记账):否则"首次 mark() 之前"分配的块不在
# chk_stack 里,事后 free 它们时 _is_valid_ptr 的活跃集检查必然判无效 →
# free 被永久拒绝 → 这些块再无可能回到空闲链 = **永久漏块**,是池单调漏满的
# 一个确定性来源(OOM 报告里若出现「块头奇数=1 但在记录窗口=0」即为实锤)。
# Lazily build the record stack (also records before the first mark()); else blocks
# allocated before it are missing from chk_stack and their free is permanently rejected.
if self.chk_stack is None:
self.mark()
if self.chk_stack is not None:
@@ -1585,16 +1583,14 @@ class MemBuddy(MemManager):
stdio.printf("[MEM] alloc 失败 size=%zu needed=%zu order=%d max_order=%d used=%zu pool_base=%llu\n",
size, needed2, order2, self.max_order, self.used_bytes(),
uint64(self.usable))
# 归属(always-on,无需 --detection):site 来自 alloc_tagged 维护的站点 tag,
# ctx=阶段/当前文件由 TPV 的 SetAllocCtx 维护(未设置则打印 ?)。只在失败路径
# 打印,不影响正常流程;缺这一行时 [MEM] 只给池基址,无法判断谁吃满了池子。
# Attribution (always-on, no --detection needed): site comes from the alloc_tagged site
# tag, ctx=stage/current file from TPV's SetAllocCtx (? if unset). Failure path only.
stdio.printf("[MEM] 归属 site=%s ctx=%s/%s 池容量=%zu\n",
_det_site_name(_alloc_site_current()),
_alloc_ctx_stage_str(), _alloc_ctx_detail_str(),
self.usable_size)
# 窗口起点空闲:mark() 时刻记下的 free_bytes。
# ≈池容量 ⇒ 本窗口把池吃满(消费点在当前文件);
# ≈0 ⇒ 进窗口前池就已满(历史丢失/常驻占用,当前文件只是受害者)。
# Window-start free bytes (recorded at mark()): ~pool size => this window filled the
# pool; ~0 => already full before the window (current file is only a victim).
win_start_free: sizet = self.mark_free_bytes
win_now_free: sizet = self.free_bytes()
win_net: sizet = 0
@@ -1602,7 +1598,7 @@ class MemBuddy(MemManager):
win_net = win_start_free - win_now_free
stdio.printf("[MEM] 本窗口起点空闲=%zu 当前空闲=%zu(窗口内净分配=%zu)\n",
win_start_free, win_now_free, win_net)
# 尺寸画像:池被谁吃满,看存活块的 order 分布(只读既有状态,无需 --detection)
# Size profile: see which orders of live blocks filled the pool (read-only, no --detection).
self.dump_alive_orders()
# self_check != 0 means the free list structure is damaged (a block header or
# next pointer was written through), so alloc finds no block despite free space.
@@ -1722,17 +1718,8 @@ class MemBuddy(MemManager):
stdio.fflush(None)
stdlib.exit(1)
else:
# _is_valid_ptr 判无效 = 该块不会被回收到空闲链(静默 no-op 是"池漏满却
# 无线索"的元凶)。首次出现即打印一行现场,之后不再刷屏。
# 三个判据逐项打出(mirror _is_valid_ptr),避免"header=0x0"这种歧义:
# in_pool=0 → free() 收到池外指针(stdlib/另一池/野指针)
# aligned=0 → 块地址未按 32 字节对齐(指针不是块首)
# hdr_odd=0 → 块头是偶数(已释放/被越界写穿)——正常 free 之后重复 free
# 或"邻居块头被写坏"都落这里,取两者的区分看「窗口内=1 且
# 该块状态异常」与后续 canary/账目行
# in_win=0 → 块头是奇数(存活)却不在 chk_stack[0, chk_depth) 窗口内
# (分配发生在首次 mark() 之前 ⇒ 记录栈里没有它 ⇒ 该 free
# 被永久拒绝 ⇒ 这块再也不会回到空闲链)
# _is_valid_ptr invalid = block never returns to a free list (silent pool leak); print once.
# Criteria: in_pool=0 not in pool, aligned=0 not 32B-aligned, hdr_odd=0 even header, in_win=0 outside window.
global _rej_free_reported
if _rej_free_reported == 0:
_rej_free_reported = 1
@@ -1760,7 +1747,7 @@ class MemBuddy(MemManager):
break
e_ci += 1
else:
e_in_win = -1 # 记录栈满(chk_depth==chk_cap)⇒ 该检查被跳过
e_in_win = -1 # record stack full (chk_depth==chk_cap) -> check skipped
stdio.printf("[MEM] free 被拒绝: ptr=%llu block=%llu 在池内=%d 对齐=%d "
"块头=0x%llX 块头奇数=%d 在记录窗口=%d chk_depth=%zu "
"本池 base=%llu usable=%llu usable_size=%zu\n",
@@ -1958,7 +1945,7 @@ class MemBuddy(MemManager):
head_val: uint64 = self.free_lists[i]
cur: rawptr = rawptr(head_val)
if uint64(cur) != 0 and self._fl_block_ok(cur) == 0:
# 链表头本身非法(如未初始化 0xBE…):直接解引用会 AV,这里报告后停止本阶。
# Free-list head itself invalid (e.g. uninitialized 0xBE...): dereferencing would AV; report and stop this order.
stdio.printf("[MEM] free_lists[%d] 头非法(非池内对齐块地址,疑元数据未初始化/被写穿): "
"head=%llu chk_depth=%zu usable=%llu usable_size=%zu\n",
i, head_val, self.chk_depth, uint64(self.usable), self.usable_size)
@@ -1974,8 +1961,8 @@ class MemBuddy(MemManager):
count += 1
next_p: rawptr = rawptr(*((uint64*)(cur)))
if uint64(next_p) != 0 and self._fl_block_ok(next_p) == 0:
# 头/节点非法(未初始化 0xBE…/非池内对齐地址):继续解引用会直接 AV。
# 这里只报告并停止本阶计数——"链表头是未初始化内存"本身就是元数据损坏的铁证。
# Head/node invalid (uninitialized 0xBE... or non-pool-aligned): further dereference
# would AV, so just report and stop counting this order (metadata corruption proof).
stdio.printf("[MEM] free_lists[%d] 节点非法(非池内对齐块地址,疑元数据未初始化/被写穿): "
"head=%llu cur=%llu next=%llu chk_depth=%zu usable=%llu usable_size=%zu\n",
i, head_val, uint64(cur), uint64(next_p), self.chk_depth,
@@ -1995,7 +1982,7 @@ class MemBuddy(MemManager):
head_val: uint64 = self.free_lists[i]
cur: rawptr = rawptr(head_val)
if uint64(cur) != 0 and self._fl_block_ok(cur) == 0:
# 链表头本身非法(如未初始化 0xBE…):直接解引用会 AV,这里报告后停止本阶。
# Free-list head itself invalid (e.g. uninitialized 0xBE...): dereferencing would AV; report and stop this order.
stdio.printf("[MEM] free_lists[%d] 头非法(非池内对齐块地址,疑元数据未初始化/被写穿): "
"head=%llu chk_depth=%zu usable=%llu usable_size=%zu\n",
i, head_val, self.chk_depth, uint64(self.usable), self.usable_size)
@@ -2099,8 +2086,8 @@ class MemBuddy(MemManager):
stdio.printf("[MEM] 整池账目:镜像存活=%zu 块/%zu 字节 空闲链表=%zu 字节 "
"丢失=%zu 字节(丢失=既不在链表也不被持有 ⇒ 空闲链被写穿后截断)\n",
live, alive_bytes, fb, lost)
# 分段:mark() 之后分配的那段才是「本窗口」。仅看总数会把历史前缀也算进来
# (mark1 在 Phase1 1b 只打一次 ⇒ 前缀=1a 的记录,窗口=整段 1b)。
# Segmentation: only the part allocated after mark() is "this window"; the total would also
# include the historical prefix (mark1 runs once in Phase1 1b => prefix=1a, window=1b).
stdio.printf("[MEM] 分段(mark_depth=%zu chk_depth=%zu):前缀[0,mark)=%zu 块/%zu 字节 "
"本窗口[mark,end)=%zu 块/%zu 字节\n",
self.mark_depth, self.chk_depth, pfx_live, pfx_bytes_a,
@@ -2131,12 +2118,11 @@ class MemBuddy(MemManager):
hv_p8: uint64 = 0
if raw >= uint64(self.usable) and raw < pool_end:
bp: rawptr = rawptr(raw)
# 同址两种读法交叉验证:hv 用直读 `*((uint64*)(p))`,hv2 用 memhub
# 其余路径通用的两步式 `uint64(rawptr(*((uint64*)(p))))`。若两者不一致,
# 说明「块头读作 0」是读法/代码生成问题,而非池真的被清零。
# Cross-check two reads at the same address: hv direct, hv2 via the two-step memhub
# form; a mismatch means "header read as 0" is a codegen/read issue, not a zeroed pool.
hv = *((uint64*)(bp))
hv2 = uint64(rawptr(*((uint64*)(bp))))
# 邻字探针:整片被零填充 ⇒ 前后邻字同样为 0(一次巨量零填充写的签名)
# Neighbor probe: an all-zero region implies the adjacent words are also 0 (signature of a huge zero-fill write).
if raw >= uint64(self.usable) + 8:
bp_m: rawptr = rawptr(raw - 8)
hv_m8 = *((uint64*)(bp_m))
@@ -2162,7 +2148,7 @@ class MemBuddy(MemManager):
head_val: uint64 = self.free_lists[i]
cur: rawptr = rawptr(head_val)
if uint64(cur) != 0 and self._fl_block_ok(cur) == 0:
# 链表头本身非法(如未初始化 0xBE…):直接解引用会 AV,这里报告后停止本阶。
# Free-list head itself invalid (e.g. uninitialized 0xBE...): dereferencing would AV; report and stop this order.
stdio.printf("[MEM] free_lists[%d] 头非法(非池内对齐块地址,疑元数据未初始化/被写穿): "
"head=%llu chk_depth=%zu usable=%llu usable_size=%zu\n",
i, head_val, self.chk_depth, uint64(self.usable), self.usable_size)
+172 -264
View File
File diff suppressed because it is too large. Load diff
+557 -590
View File
File diff suppressed because it is too large. Load diff
+59 -76
View File
@@ -16,7 +16,7 @@ import viperlib
# * 名字保留 LLVMxxx(同形优先)。语义差异见 README.md。
# ============================================================
# 导入子模块
# Import submodules
from .__types import (
LLVMType, ParamNode, LLVMTypeCore,
Int1, Int8, Int16, Int32, Int64, Ptr, Func, Void, Array, Struct,
@@ -95,10 +95,10 @@ from .__verify import (
# ============================================================
# 便捷工厂: 字符串复制
# Convenience factory: string copy
# ============================================================
def _str_dup(pool: memhub.MemBuddy*, src: str) -> str:
"""从 pool 分配并复制一个 C 字符串"""
"""Allocate from pool and copy a C string."""
if src is None: return None
slen: sizet = string.strlen(src)
buf: str = pool.alloc(slen + 1)
@@ -108,15 +108,13 @@ def _str_dup(pool: memhub.MemBuddy*, src: str) -> str:
# ============================================================
# 便捷工厂: 创建带名称的基本块
# Convenience factory: create a named basic block
# ============================================================
def create_block(pool: memhub.MemBuddy*, func: rawptr[Function],
name: str) -> rawptr[BasicBlock]:
"""创建基本块并添加到函数"""
# 块名与函数参数值名冲突防护:LLVM 中块标签 "entry:" 即值 %entry,
# 与参数 %entry 重名 → llc "unable to create block named 'entry'"
# (CallGenCall._field_byte_offset(self, pool, entry, ...) 根因)。
# 冲突时追加 .1/.2/... 后缀去重(Param.Name 可能带 % 前缀,剥离比较)。
"""Create a basic block and add it to the function."""
# Block-name/param conflict guard: block label "entry:" is value %entry, so a param %entry
# collides; on collision append .1/.2/... (strip a leading % from Param.Name before comparing).
use_name: str = name
if func is not None and func.Params is not None and name is not None \
and name[0] != '\0':
@@ -150,29 +148,24 @@ def create_block(pool: memhub.MemBuddy*, func: rawptr[Function],
# ============================================================
# 便捷工厂: 创建整数常量值
# Convenience factory: create an integer constant value
# ============================================================
def const_int(pool: memhub.MemBuddy*, bits: int,
val: int64) -> rawptr[Value]:
"""创建整数常量值(自动创建类型 + 名字文本)
参数:
bits: 位宽 (1/8/16/32/64)
val: 整数值
"""Create an integer constant value (auto-creates type + name text).
bits: bit width (1/8/16/32/64); val: integer value.
"""
# 创建类型
# create type
ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__())
if ty is None: return None
string.memset(ty, 0, LLVMType.__sizeof__())
c.DerefAs(ty, LLVMType.Int(bits))
# 创建名字文本
# create name text
name: str = pool.alloc(32)
if name is None: return None
# LLVM i64 字面量范围不含 INT64_MIN(-9223372036854775808):该值必须
# 用无符号位模式 9223372036854775808 表示(LLVM 接受),否则 opt 解析
# "-9223372036854775808" 报 expected value token(二代编 viperlib.vp
# u_num=0x8000000000000000 崩溃根因;gen1/TPC 即输出无符号位模式)。
# LLVM i64 literals exclude INT64_MIN: represent it as the unsigned bit pattern
# 9223372036854775808 (accepted by LLVM), else opt rejects "-9223372036854775808".
if bits == 64 and val == -9223372036854775807 - 1:
viperlib.snprintf(name, 32, "9223372036854775808")
else:
@@ -182,21 +175,21 @@ def const_int(pool: memhub.MemBuddy*, bits: int,
def const_int32(pool: memhub.MemBuddy*, val: int64) -> rawptr[Value]:
"""便捷: 创建 i32 整数常量"""
"""Convenience: create an i32 integer constant."""
return const_int(pool, 32, val)
def const_int64(pool: memhub.MemBuddy*, val: int64) -> rawptr[Value]:
"""便捷: 创建 i64 整数常量"""
"""Convenience: create an i64 integer constant."""
return const_int(pool, 64, val)
# ============================================================
# 便捷工厂: 创建函数并添加到模块
# Convenience factory: create a function and add it to the module
# ============================================================
def create_function(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]:
"""创建函数并添加到模块"""
"""Create a function and add it to the module."""
name_dup: str = _str_dup(pool, name)
func: rawptr[Function] = new_function(pool, name_dup, ret_ty)
if func is not None:
@@ -206,7 +199,7 @@ def create_function(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
def add_param(pool: memhub.MemBuddy*, func: rawptr[Function],
ty: rawptr[LLVMType], name: str) -> rawptr[Param]:
"""创建参数并添加到函数"""
"""Create a parameter and add it to the function."""
name_dup: str = _str_dup(pool, name)
param: rawptr[Param] = new_param(pool, ty, name_dup)
if param is not None:
@@ -215,14 +208,12 @@ def add_param(pool: memhub.MemBuddy*, func: rawptr[Function],
# ============================================================
# 便捷工厂: 创建函数声明(declare)
# Convenience factory: create a function declaration (declare)
# ============================================================
def create_declare(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
name: str, ret_ty: rawptr[LLVMType]) -> rawptr[Function]:
"""创建函数声明(declare,无函数体)并添加到模块
与 create_function 不同,声明不生成函数体,仅生成:
declare <ret_ty> @<name>(<params>)
"""Create a function declaration (declare, no body) and add it to the module.
Unlike create_function, it emits only: declare <ret_ty> @<name>(<params>)
"""
name_dup: str = _str_dup(pool, name)
func: rawptr[Function] = new_function(pool, name_dup, ret_ty)
@@ -233,51 +224,45 @@ def create_declare(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
# ============================================================
# 便捷工厂: 创建字符串常量全局变量
# Convenience factory: create a string-constant global variable
#
# 初值以 llvmlite 同形的 **LLVM 语法文本** 存入 gv.Initializer
# (c"contents\00"),MLIR 渲染阶段(__module.vp/_MlirGlobalInit)
# 再转成 MLIR attribute 形态 "contents"。
# The initializer is stored in gv.Initializer as llvmlite-shaped LLVM syntax text (c"contents\00");
# the MLIR render stage (__module.vp/_MlirGlobalInit) converts it to the attribute form "contents".
# ============================================================
def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
name: str, text: str) -> rawptr[GlobalVariable]:
"""创建字符串常量全局变量并添加到模块
参数:
name: 全局变量名(如 ".str")
text: 字符串内容(C 字符串,不含末尾 NUL,NUL 由本函数自动添加)
返回: GlobalVariable 指针,其类型为 [len+1 x i8]
"""Create a string-constant global variable and add it to the module.
name: global name (e.g. ".str"); text: C string without trailing NUL (added automatically).
Returns: GlobalVariable pointer of type [len+1 x i8].
"""
if text is None: return None
slen: sizet = string.strlen(text)
count: int = slen + 1 # 含末尾 NUL
count: int = slen + 1 # includes the trailing NUL
# 创建 i8 类型
# create i8 type
i8_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__())
if i8_ty is None: return None
string.memset(i8_ty, 0, LLVMType.__sizeof__())
c.DerefAs(i8_ty, LLVMType.Int(8))
# 创建 [count x i8] 数组类型
# create [count x i8] array type
arr_ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__())
if arr_ty is None: return None
string.memset(arr_ty, 0, LLVMType.__sizeof__())
c.DerefAs(arr_ty, LLVMType.Array(i8_ty, count))
# 创建全局变量
# create global variable
name_dup: str = _str_dup(pool, name)
gv: rawptr[GlobalVariable] = new_global_variable(pool, name_dup, arr_ty)
if gv is None: return None
# 设置属性: private + unnamed_addr + constant
# set attributes: private + unnamed_addr + constant
global_set_linkage(gv, "private")
global_set_unnamed_addr(gv, 1)
global_set_constant(gv, 1)
# 构造初始化值文本: c"contents\00"
# LLVM IR 字符串字面量格式: c"<chars>" 其中 " 和 \ 及非打印字符用 \xx 转义
# 缓冲区大小: 最坏情况每个字符4字节(\xx)+ c"..." + \00 + "
# Build initializer text c"contents\00": LLVM IR string literal c"<chars>" with " \ and
# non-printables escaped as \xx; buffer sized for worst case (4 bytes/char) + c"..." + \00 + "
init_buf: str = pool.alloc(slen * 4 + 8)
if init_buf is None: return None
pos: sizet = 0
@@ -288,7 +273,7 @@ def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
for i in range(slen):
ch: uint8 = text[i]
if ch == 0x22 or ch == 0x5C or ch < 0x20 or ch >= 0x7F:
# 需要转义: " → \22, \ → \5c, 非打印字符 → \xx
# escape: " -> \22, \ -> \5c, non-printable -> \xx
init_buf[pos] = '\\'
pos += 1
hi: int = (ch >> 4) & 0x0F
@@ -306,7 +291,7 @@ def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
else:
init_buf[pos] = ch
pos += 1
# 末尾 NUL 字节
# trailing NUL byte
init_buf[pos] = '\\'
pos += 1
init_buf[pos] = '0'
@@ -323,16 +308,14 @@ def create_global_string(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
# ============================================================
# 便捷工厂: 创建带初始化值的整数全局变量
# Convenience factory: create an integer global with an initializer
# ============================================================
def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
name: str, bits: int, val: int64) -> rawptr[GlobalVariable]:
"""创建整数全局变量并添加到模块
MLIR 渲染结果:
llvm.mlir.global @<name>(<val> : i<bits>) : i<bits>
"""Create an integer global variable and add it to the module.
MLIR output: llvm.mlir.global @<name>(<val> : i<bits>) : i<bits>
"""
# 创建整数类型
# create integer type
ty: rawptr[LLVMType] = pool.alloc(LLVMType.__sizeof__())
if ty is None: return None
string.memset(ty, 0, LLVMType.__sizeof__())
@@ -342,7 +325,7 @@ def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
gv: rawptr[GlobalVariable] = new_global_variable(pool, name_dup, ty)
if gv is None: return None
# 构造初始化值文本: "42" (类型由全局渲染阶段单独打印)
# build initializer text "42" (the type is printed separately by the global render stage)
init_buf: str = pool.alloc(48)
if init_buf is None: return None
viperlib.snprintf(init_buf, 48, "%lld", val)
@@ -353,9 +336,9 @@ def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
# ============================================================
# LLVMModuleCore: 模块/函数/值创建核心,封装 pool 避免重复传参
# LLVMModuleCore: module/function/value creation core; wraps pool to avoid passing it repeatedly
#
# 用法:
# Usage:
# mcore: rawptr[LLVMModuleCore] = LLVMModuleCore(pool)
# mod: rawptr[LLVMModule] = mcore.NewModule("test")
# func: rawptr[Function] = mcore.CreateFunction(mod, "add", ty_i32)
@@ -363,13 +346,13 @@ def create_global_int(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule],
# ============================================================
@t.NoVTable
class LLVMModuleCore:
"""模块/函数/值创建核心,封装 pool 避免每次创建都传 pool"""
"""Module/function/value creation core; wraps pool so callers need not pass it."""
Pool: memhub.MemBuddy*
def __init__(self, pool: memhub.MemBuddy*):
self.Pool = pool
# ---- 模块操作 ----
# ---- module operations ----
def NewModule(self, name: str) -> rawptr[LLVMModule]:
return new_module(self.Pool, name)
@@ -379,7 +362,7 @@ class LLVMModuleCore:
def CreateDeclare(self, mod: rawptr[LLVMModule], name: str,
ret_ty: rawptr[LLVMType]) -> rawptr[Function]:
"""创建函数声明(declare,无函数体)"""
"""Create a function declaration (declare, no body)."""
return create_declare(self.Pool, mod, name, ret_ty)
def AddParam(self, func: rawptr[Function], ty: rawptr[LLVMType],
@@ -387,11 +370,11 @@ class LLVMModuleCore:
return add_param(self.Pool, func, ty, name)
def FunctionSetAttrs(self, func: rawptr[Function], attrs: str):
"""设置函数级属性文本"""
"""Set the function-level attribute text."""
function_set_attrs(func, attrs)
def ParamSetAttrs(self, param: rawptr[Param], attrs: str):
"""设置参数属性文本"""
"""Set the parameter attribute text."""
param_set_attrs(param, attrs)
def CreateBlock(self, func: rawptr[Function],
@@ -401,10 +384,10 @@ class LLVMModuleCore:
def NewBuilder(self, func: rawptr[Function]) -> rawptr[IRBuilder]:
return new_builder(self.Pool, func)
# ---- 全局变量 ----
# ---- globals ----
def NewGlobal(self, mod: rawptr[LLVMModule], name: str,
ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]:
"""创建全局变量并添加到模块"""
"""Create a global variable and add it to the module."""
name_dup: str = _str_dup(self.Pool, name)
gv: rawptr[GlobalVariable] = new_global_variable(self.Pool, name_dup, ty)
if gv is not None:
@@ -413,12 +396,12 @@ class LLVMModuleCore:
def CreateGlobalString(self, mod: rawptr[LLVMModule], name: str,
text: str) -> rawptr[GlobalVariable]:
"""创建字符串常量全局变量"""
"""Create a string-constant global variable."""
return create_global_string(self.Pool, mod, name, text)
def CreateGlobalInt(self, mod: rawptr[LLVMModule], name: str,
bits: int, val: int64) -> rawptr[GlobalVariable]:
"""创建带初始化值的整数全局变量"""
"""Create an integer global with an initializer."""
return create_global_int(self.Pool, mod, name, bits, val)
def GlobalSetConstant(self, gv: rawptr[GlobalVariable], is_const: int):
@@ -433,7 +416,7 @@ class LLVMModuleCore:
def GlobalSetSection(self, gv: rawptr[GlobalVariable], section: str):
global_set_section(gv, section)
# ---- 值创建 ----
# ---- value creation ----
def ConstInt(self, bits: int, val: int64) -> rawptr[Value]:
return const_int(self.Pool, bits, val)
@@ -452,7 +435,7 @@ class LLVMModuleCore:
def ConstNull(self, ty: rawptr[LLVMType], name: str) -> rawptr[Value]:
return ConstNull(self.Pool, ty, name)
# ---- 打印 ----
# ---- printing ----
def PrintValue(self, buf: str, size: sizet, val: rawptr[Value]):
ValuePrint(buf, size, val, self.Pool)
@@ -462,17 +445,17 @@ class LLVMModuleCore:
def PrintModule(self, buf: str, size: sizet, mod: rawptr[LLVMModule]):
LLVMModulePrint(buf, size, mod, self.Pool, OUTPUT_FULL)
# ---- SSA 验证 ----
# ---- SSA verification ----
def NewVerifyResult(self, buf_size: sizet) -> rawptr[VerifyResult]:
"""创建验证结果对象(buf_size 为错误消息缓冲区大小)"""
"""Create a verify-result object (buf_size = error message buffer size)."""
return _new_verify_result(self.Pool, buf_size)
def VerifyFunction(self, func: rawptr[Function],
result: rawptr[VerifyResult]) -> int:
"""验证单个函数的 SSA 正确性,返回错误数(0=通过)"""
"""Verify SSA correctness of one function; returns error count (0 = pass)."""
return verify_function(self.Pool, func, result)
def VerifyModule(self, mod: rawptr[LLVMModule],
result: rawptr[VerifyResult]) -> int:
"""验证模块中所有函数的 SSA 正确性,返回错误数(0=通过)"""
"""Verify SSA correctness of all functions in the module; returns error count (0 = pass)."""
return verify_module(self.Pool, mod, result)
+310 -441
View File
File diff suppressed because it is too large. Load diff
+201 -351
View File
File diff suppressed because it is too large. Load diff
+43 -67
View File
@@ -19,21 +19,21 @@ from .__types import LLVMType, TypePrint
# ============================================================
@t.NoVTable
class Value(GSListNode[Value]):
Ty: rawptr[LLVMType] # 值的类型
Name: str # SSA 名("%0"/"%result")或常量文本("42"/"1.5e+00")
IsConst: int # 1=常量字面量, 0=SSA 临时值
IntVal: int64 # 整数常量值(当 IsConst=1 时使用)
FloatVal: double # 浮点常量值(当 IsConst=1 且 Ty 为 Float 时使用)
Ty: rawptr[LLVMType] # value type
Name: str # SSA name ("%0"/"%result") or constant text ("42"/"1.5e+00")
IsConst: int # 1 = constant literal, 0 = SSA temporary
IntVal: int64 # integer constant value (used when IsConst=1)
FloatVal: double # float constant value (used when IsConst=1 and Ty is Float)
# ============================================================
# 工厂函数
# Factory functions
# ============================================================
# Value 硬编码大小: Next(8) + Ty(8) + Name(8) + IsConst(4) + IntVal(8) + FloatVal(8) = 48 字节
# Value hardcoded size: Next(8) + Ty(8) + Name(8) + IsConst(4) + IntVal(8) + FloatVal(8) = 48 bytes
define VALUE_SIZE = 48
def new_value(pool: memhub.MemBuddy*) -> rawptr[Value]:
"""从 mpool 分配一个零初始化的 Value"""
"""Allocate a zero-initialized Value from mpool."""
ptr: rawptr[Value] = pool.alloc(VALUE_SIZE)
if ptr:
string.memset(ptr, 0, VALUE_SIZE)
@@ -42,12 +42,8 @@ def new_value(pool: memhub.MemBuddy*) -> rawptr[Value]:
def ConstInt(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
val: int64, name: str) -> rawptr[Value]:
"""创建整数常量值
参数:
ty: 整数类型(Int1/Int8/Int32/Int64)
val: 整数值
name: 常量文本(如 "42"),由调用方提供
"""Create an integer constant value.
ty: integer type (Int1/Int8/Int32/Int64); val: integer value; name: caller-provided constant text (e.g. "42").
"""
v: rawptr[Value] = new_value(pool)
if v is None: return None
@@ -60,7 +56,7 @@ def ConstInt(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
def ConstNull(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
name: str) -> rawptr[Value]:
"""创建 null 指针常量"""
"""Create a null pointer constant."""
v: rawptr[Value] = new_value(pool)
if v is None: return None
v.Ty = ty
@@ -71,7 +67,7 @@ def ConstNull(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
def ConstZero(pool: memhub.MemBuddy*, ty: rawptr[LLVMType]) -> rawptr[Value]:
"""创建 zeroinitializer 常量(零初始化聚合类型:结构体/数组)"""
"""Create a zeroinitializer constant (zero-initialized aggregate: struct/array)."""
v: rawptr[Value] = new_value(pool)
if v is None: return None
v.Ty = ty
@@ -83,19 +79,10 @@ def ConstZero(pool: memhub.MemBuddy*, ty: rawptr[LLVMType]) -> rawptr[Value]:
def ConstFloat(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
val: double) -> rawptr[Value]:
"""创建浮点常量值
"""Create a float constant value.
参数:
ty: 浮点类型(Half/Float/Double/FP128)
val: 浮点数值
名字格式化为浮点**位模式**文本(f16 → `0x%04x`,f32 → `0x%08x`,
f64 → `0x%016llx`,f128 → `0x%032x` 高低两字拼接):
MLIR 的 FloatAttr 按底层二进制位解释十六进制字面量,位模式因此无精度损失。
原因: %.17e 科学计数法文本经 llc 解析对部分值产生 ±1 ulp 误差
(如 "1.10000000000000000e+01" 解析为 11.000000000000002 而非 11.0,
导致 add_scalar[0]==11 等浮点常量比较失败)。位模式无精度损失。
原 %e 默认 6 位小数还会截断精度(3.1415926535897931 -> 3.141593e+00)。
用 CRT stdio.snprintf: viperlib 自实现 %g 有小数位反转 bug。
ty: float type; val: float value. Name is the raw bit pattern (f16 0x%04x, f32 0x%08x,
f64 0x%016llx, f128 0x%032x hi+lo) so MLIR FloatAttr is lossless; use CRT stdio.snprintf (%g in viperlib is buggy).
"""
v: rawptr[Value] = new_value(pool)
if v is None: return None
@@ -110,24 +97,21 @@ def ConstFloat(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
name_buf: str = pool.alloc(64)
if name_buf is not None:
if f_bits == 16:
# half:先按 half 舍入(double → half 为 fptrunc),再取其 2 字节位模式
# half: round to half first (double -> half is fptrunc), then take its 2-byte bit pattern
h16: t.CFloat16T = t.CFloat16T(val)
b16: uint16 = 0
*&b16 = *&h16
stdio.snprintf(name_buf, 64, "0x%04x", b16)
elif f_bits == 32:
# f32 位模式必须取 **float 的 32 位编码**,不是 double 位模式的低 32 位
# (3.14 的 double 是 0x40091EB851EB851F,低 32 位 0x51EB851F 并非 f32
# 编码 0x4048F5C3)。先把值按 f32 舍入再取其 4 字节编码。
# f32 bit pattern must be the float 32-bit encoding, not the low 32 bits of the double
# (3.14 double=0x40091EB851EB851F, low 0x51EB851F != f32 0x4048F5C3): round to f32 first.
f32v: float = float(val)
b32: uint32 = 0
*&b32 = *&f32v
stdio.snprintf(name_buf, 64, "0x%08x", b32)
elif f_bits == 128:
# quad:double → fp128 为无损扩展(fpext)。小端内存里低 64 位在前、
# 高 64 位在后;文本按大端(高位在前)拼成 32 位十六进制。
# 注意 c.Load 的宽度取**源指针**的 pointee,故源必须显式转成 u64*
# (否则会按 fp128 的 16 字节写入 8 字节目标 → 越界)。
# quad: double -> fp128 is lossless (fpext); little-endian low 64 first, print big-endian
# (hi first). c.Load width follows the source pointee, so cast the source to u64* to avoid overflow.
q128: t.CFloat128T = t.CFloat128T(val)
q8: rawptr[uint8] = (rawptr[uint8])(&q128)
q_lo: uint64 = 0
@@ -145,11 +129,8 @@ def ConstFloat(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
def SSAValue(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
name: str) -> rawptr[Value]:
"""创建 SSA 临时值
参数:
ty: 值类型
name: SSA 名(如 "%0"/"%result"),由调用方提供
"""Create an SSA temporary value.
ty: value type; name: caller-provided SSA name (e.g. "%0"/"%result").
"""
v: rawptr[Value] = new_value(pool)
if v is None: return None
@@ -161,20 +142,16 @@ def SSAValue(pool: memhub.MemBuddy*, ty: rawptr[LLVMType],
# ============================================================
# ValuePrint: 将值序列化为 MLIR 文本(仅供诊断)
# ValuePrint: serialize a value to MLIR text (diagnostics only)
#
# 渲染规则:
# SSA 值 -> "%0"(MLIR 中操作数就是 SSA 名,不附带类型)
# 常量 -> "42 : i32"(MLIR attribute 文本形态)
#
# 注意: 指令渲染**不得**依赖本函数。函数体的 op 渲染走 __function.vp 的
# 操作数解析路径(常量必须先物化为 llvm.mlir.constant/zero/addressof op)。
# SSA value -> "%0" (operands are bare SSA names); constant -> "42 : i32" (MLIR attribute form).
# Instruction rendering MUST NOT depend on this; op rendering goes through __function.vp (constants
# must first be materialized as llvm.mlir.constant/zero/addressof ops).
# ============================================================
def ValuePrint(buf: str, size: sizet,
val: rawptr[Value], pool: memhub.MemBuddy*):
"""将值 val 序列化为 MLIR 文本写入 buf
pool 用于复杂类型(Array/Struct/Func)递归打印时分配临时缓冲区。
"""Serialize val into buf as MLIR text; pool is used to allocate scratch buffers for
recursive complex types (Array/Struct/Func).
"""
if val is None or buf is None or size == 0:
return
@@ -182,10 +159,10 @@ def ValuePrint(buf: str, size: sizet,
if val.Name is None:
return
if val.IsConst == 0:
# SSA 值: 只输出 SSA 名
# SSA value: emit only the SSA name
_append_str(buf, size, val.Name)
return
# 常量: "<常量文本> : <MLIR 类型>"
# constant: "<constant text> : <MLIR type>"
_append_str(buf, size, val.Name)
_append_str(buf, size, " : ")
tp: str = pool.alloc(4096)
@@ -195,63 +172,62 @@ def ValuePrint(buf: str, size: sizet,
# ============================================================
# Value 字段访问器(供其他模块绕过 stub 类型限制使用)
# Value field accessors (let other modules bypass stub-type limits)
#
# 跨模块编译时,使用方模块只有 Value 的 stub 类型(字段不足或偏移错误),
# 导致 TransPyC 静默跳过字段访问(不生成 GEP,不报错)。
# 这些访问器在拥有完整类型定义的本模块内执行,能正确访问所有字段。
# Cross-module callers only see a stub Value (missing/wrong fields), so TransPyC silently skips
# field access; these accessors run inside this module with the full type definition.
# ============================================================
def value_get_ty(val: rawptr[Value]) -> rawptr[LLVMType]:
"""获取值的类型"""
"""Get the value's type."""
if val is None:
return None
return val.Ty
def value_get_next(val: rawptr[Value]) -> rawptr[Value]:
"""获取链表下一个值"""
"""Get the next value in the list."""
if val is None:
return None
return val.Next
def value_set_ty(val: rawptr[Value], ty: rawptr[LLVMType]):
"""设置值的类型"""
"""Set the value's type."""
if val is None:
return
val.Ty = ty
def value_set_next(val: rawptr[Value], nxt: rawptr[Value]):
"""设置链表下一个值"""
"""Set the next value in the list."""
if val is None:
return
val.Next = nxt
def value_set_name(val: rawptr[Value], name: str):
"""设置值的名字"""
"""Set the value's name."""
if val is None:
return
val.Name = name
def value_set_isconst(val: rawptr[Value], is_const: int):
"""设置是否为常量"""
"""Set whether the value is a constant."""
if val is None:
return
val.IsConst = is_const
# ============================================================
# 内部辅助
# Internal helpers
# ============================================================
def _append_str(dst: str, dst_size: sizet, src: str):
"""将 src 追加到 dst 末尾"""
"""Append src to the end of dst."""
if dst is None or src is None: return
dlen: sizet = string.strlen(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
# Unsigned-underflow guard: if dlen >= buffer size (buffer corrupt/dangling) return early,
# else remain = dst_size - dlen underflows and dst[dlen+i] would write past the buffer.
if dlen >= dst_size:
return
slen: sizet = string.strlen(src)
+63 -81
View File
@@ -22,7 +22,7 @@ from .__module import LLVMModule
# 4. Phi 位置: phi 指令只能出现在块首(非 phi 指令之前)
# ============================================================
# 错误码
# Error codes
define VERIFY_OK = 0
define VERIFY_ERR_NO_TERMINATOR = 1
define VERIFY_ERR_DUPLICATE_DEF = 2
@@ -31,32 +31,32 @@ define VERIFY_ERR_PHI_NOT_FIRST = 4
# ============================================================
# NameEntry: 已定义 SSA 名条目(链表节点)
# NameEntry: defined SSA name entry (linked-list node)
#
# 注意: 必须加 @t.NoVTable(理由见 __function.py 的 Line 注释)。
# NOTE: must carry @t.NoVTable (see the Line comment in __function.vp).
# ============================================================
@t.NoVTable
class NameEntry(GSListNode[NameEntry]):
Name: str # SSA 名(如 "%0"/"%result")
Name: str # SSA name (e.g. "%0"/"%result")
# ============================================================
# VerifyResult: 验证结果
# VerifyResult: verification result
# ============================================================
class VerifyResult:
ErrorCount: int # 错误总数
WarningCount: int # 警告总数
ErrorCode: int # 第一个错误码
ErrorBuf: str # 错误消息缓冲区
ErrorBufSize: sizet # 缓冲区大小
ErrorCount: int # total error count
WarningCount: int # total warning count
ErrorCode: int # first error code
ErrorBuf: str # error message buffer
ErrorBufSize: sizet # buffer size
# ============================================================
# 辅助函数
# Helper functions
# ============================================================
def _new_verify_result(pool: memhub.MemBuddy*,
buf_size: sizet) -> rawptr[VerifyResult]:
"""创建验证结果对象"""
"""Create a VerifyResult object"""
ptr: rawptr[VerifyResult] = pool.alloc(VerifyResult.__sizeof__())
if ptr is None: return None
string.memset(ptr, 0, VerifyResult.__sizeof__())
@@ -72,7 +72,7 @@ def _new_verify_result(pool: memhub.MemBuddy*,
def _report_error(result: rawptr[VerifyResult], code: int, msg: str):
"""记录一个错误"""
"""Record one error"""
if result is None: return
result.ErrorCount += 1
if result.ErrorCode == VERIFY_OK:
@@ -82,24 +82,23 @@ def _report_error(result: rawptr[VerifyResult], code: int, msg: str):
_append_cstr(result.ErrorBuf, result.ErrorBufSize, "\n")
# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位,
# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。
# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。
# _append_cstr write-position cache: O(1) append into the same buffer, avoiding
# O(n^2) strlen(dst) rescans; falls back to strlen on buffer switch or reset.
_g_last_dst: uint64 = 0
_g_last_pos: sizet = 0
def _append_cstr(dst: str, dst_size: sizet, src: str):
"""将 src 追加到 dst 末尾"""
"""Append src to the end of dst"""
global _g_last_dst, _g_last_pos
if dst is None or src is None: return
dlen: sizet = 0
if _g_last_dst == uint64(dst):
dlen = _g_last_pos
# 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部
# Buffer reset (caller reuses same address after buf[0]=0) -> restart from head
if dlen > 0 and dst[0] == '\0':
dlen = 0
# 缓存一致性校验(同 __module/__function 版):缓存 pos 过期会追加到首个 NUL
# 之后使内容不可见,校验失败回退 strlen;正常热路径仍 O(1)。
# Cache-consistency check (same as __module/__function): a stale cached pos would append
# past the first NUL and hide content; fall back to strlen on failure, hot path stays O(1).
if dlen == 0:
if dst[0] != '\0':
dlen = string.strlen(dst)
@@ -108,8 +107,8 @@ def _append_cstr(dst: str, dst_size: sizet, src: str):
else:
dlen = string.strlen(dst)
_g_last_dst = uint64(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
# Unsigned-underflow guard: if dlen exceeds the buffer size (corrupted/dangling buffer),
# return; otherwise remain = dst_size - dlen underflows and writes out of bounds.
if dlen >= dst_size:
return
slen: sizet = string.strlen(src)
@@ -124,31 +123,31 @@ def _append_cstr(dst: str, dst_size: sizet, src: str):
# ============================================================
# SSA 名表操作
# SSA name-table operations
# ============================================================
def _new_name_table(pool: memhub.MemBuddy*) -> rawptr[GSList[NameEntry]]:
"""创建空名表"""
"""Create an empty name table"""
gsl: rawptr[GSList[NameEntry]] = pool.alloc(24) # GSList: Head+Tail+Count
if gsl is None: return None
string.memset(gsl, 0, 24)
return gsl
# NameEntry 硬编码大小: Next(8) + Name(8) = 16 字节
# NameEntry hardcoded size: Next(8) + Name(8) = 16 bytes
define NENTRY_SIZE = 16
def _name_table_add(table: rawptr[GSList[NameEntry]],
pool: memhub.MemBuddy*,
name: str) -> int:
"""添加名到表。返回 1=重复, 0=成功添加"""
"""Add a name to the table. Returns 1=duplicate, 0=added"""
if table is None or name is None: return 0
# 检查重复
# Check for duplicates
cur: rawptr[NameEntry] = table.Head
while cur is not None:
if string.strcmp(cur.Name, name) == 0:
return 1
cur = cur.Next
# 添加新条目
# Add new entry
entry: rawptr[NameEntry] = pool.alloc(NENTRY_SIZE)
if entry is None: return 0
string.memset(entry, 0, NENTRY_SIZE)
@@ -159,7 +158,7 @@ def _name_table_add(table: rawptr[GSList[NameEntry]],
def _name_table_contains(table: rawptr[GSList[NameEntry]],
name: str) -> int:
"""检查名是否在表中。返回 1=存在, 0=不存在"""
"""Return 1 if name is in the table, else 0"""
if table is None or name is None: return 0
cur: rawptr[NameEntry] = table.Head
while cur is not None:
@@ -170,10 +169,10 @@ def _name_table_contains(table: rawptr[GSList[NameEntry]],
# ============================================================
# 文本分析辅助
# Text analysis helpers
# ============================================================
def _is_char_name(ch: char) -> int:
"""判断字符是否为 SSA 名字符 [a-zA-Z0-9._]"""
"""Return 1 if ch is a SSA name char [a-zA-Z0-9._]"""
if ch >= 'a' and ch <= 'z': return 1
if ch >= 'A' and ch <= 'Z': return 1
if ch >= '0' and ch <= '9': return 1
@@ -183,18 +182,16 @@ def _is_char_name(ch: char) -> int:
def _extract_def_name(pool: memhub.MemBuddy*,
text: str) -> str:
"""从定义行提取被定义的 SSA 名。
定义行格式: "%<name> = <instruction>"
返回分配的 NUL 结尾名串(如 "%0"),或 None 若非定义行。
"""Extract the defined SSA name from "%<name> = <instruction>".
Returns an allocated NUL-terminated name (e.g. "%0"), or None if not a definition line.
"""
if text is None: return None
if text[0] != '%': return None
# 扫描 " = " 模式
# Scan for the " = " pattern
i: int = 1
while text[i] != '\0':
if text[i] == ' ' and text[i + 1] == '=' and text[i + 2] == ' ':
# 找到 " = " 在位置 i
# Found " = " at position i
name_len: int = i
name: str = pool.alloc(name_len + 1)
if name is None: return None
@@ -207,7 +204,7 @@ def _extract_def_name(pool: memhub.MemBuddy*,
def _is_terminator(text: str) -> int:
"""判断文本是否为终止指令"""
"""Return 1 if text is a terminator instruction"""
if text is None: return 0
if string.strncmp(text, "ret", 3) == 0: return 1
if string.strncmp(text, "br ", 3) == 0: return 1
@@ -219,7 +216,7 @@ def _is_terminator(text: str) -> int:
def _is_phi(text: str) -> int:
"""判断文本是否为 phi 指令"""
"""Return 1 if text is a phi instruction"""
if text is None: return 0
if string.strstr(text, " = phi ") is not None: return 1
return 0
@@ -228,28 +225,24 @@ def _is_phi(text: str) -> int:
def _check_uses_in_line(table: rawptr[GSList[NameEntry]],
text: str,
def_name: str) -> int:
"""检查行中所有 SSA 名使用是否已定义。
跳过 def_name(定义位置的名字)。
返回未定义使用数量。
"""Check that all SSA name uses in the line are defined.
Skips def_name; returns the number of undefined uses.
"""
if text is None: return 0
i: int = 0
undef_count: int = 0
while text[i] != '\0':
if text[i] == '%':
# 提取名
# Extract name
name_start: int = i
i += 1
while text[i] != '\0' and _is_char_name(text[i]) == 1:
i += 1
name_len: int = i - name_start
if name_len > 1:
# 构建临时名串进行比较
# 由于无法在栈上分配,用 strstr 进行简化检查
# 这里采用逐字符比较方式
# Build no temp string (no stack alloc); compare char by char.
if _name_matches_at(text, name_start, name_len, table) == 0:
# 检查是否是 def_name(定义本身的位置)
# Check whether this is def_name (the definition site itself)
if def_name is not None:
if _str_matches_at(text, name_start, name_len, def_name) == 0:
undef_count += 1
@@ -262,9 +255,7 @@ def _check_uses_in_line(table: rawptr[GSList[NameEntry]],
def _name_matches_at(text: str, start: int, length: int,
table: rawptr[GSList[NameEntry]]) -> int:
"""检查 text[start:start+length] 是否匹配名表中的某个条目。
返回 1=匹配, 0=不匹配
"""
"""Return 1 if text[start:start+length] matches an entry in the name table, else 0."""
if table is None: return 0
cur: rawptr[NameEntry] = table.Head
while cur is not None:
@@ -277,11 +268,9 @@ def _name_matches_at(text: str, start: int, length: int,
def _str_matches_at(text: str, start: int, length: int,
target: str) -> int:
"""检查 text[start:start+length] 是否等于 target 字符串。
返回 1=匹配, 0=不匹配
"""
"""Return 1 if text[start:start+length] equals target, else 0."""
if text is None or target is None: return 0
# 检查 target 长度
# Check target length
target_len: sizet = string.strlen(target)
if target_len != length: return 0
for i in range(length):
@@ -290,27 +279,21 @@ def _str_matches_at(text: str, start: int, length: int,
# ============================================================
# 主验证函数
# Main verification functions
# ============================================================
def verify_function(pool: memhub.MemBuddy*,
func: rawptr[Function],
result: rawptr[VerifyResult]) -> int:
"""验证函数的 SSA 正确性。
参数:
pool: 内存池
func: 要验证的函数
result: 验证结果(调用前需用 _new_verify_result 创建)
返回: 错误数量(0 = 验证通过)
"""Verify a function's SSA correctness; result must be created via
_new_verify_result. Returns the error count (0 = pass).
"""
if func is None or result is None: return 1
# 创建名表
# Create name table
name_table: rawptr[GSList[NameEntry]] = _new_name_table(pool)
if name_table is None: return 1
# 1. 将函数参数加入名表(参数是已定义的)
# 1. Add function params to the name table (params are defined)
params: rawptr[GSList[Param]] = func.Params
param: rawptr[Param] = None
if params is not None:
@@ -320,7 +303,7 @@ def verify_function(pool: memhub.MemBuddy*,
_name_table_add(name_table, pool, param.Name)
param = param.Next
# 1b. 将基本块名加入名表(块标签如 %then 是 br/phi 的合法引用目标)
# 1b. Add block names to the table (block labels like %then are valid br/phi targets)
blocks: rawptr[GSList[BasicBlock]] = func.Blocks
blk_iter: rawptr[BasicBlock] = None
if blocks is not None:
@@ -330,7 +313,7 @@ def verify_function(pool: memhub.MemBuddy*,
blk_name_len: sizet = string.strlen(blk_iter.Name)
blk_ref: str = pool.alloc(blk_name_len + 2)
if blk_ref is not None:
# 构造 "%<name>" 字符串
# Build the "%<name>" string
blk_ref[0] = '%'
for k in range(blk_name_len):
blk_ref[k + 1] = blk_iter.Name[k]
@@ -338,14 +321,14 @@ def verify_function(pool: memhub.MemBuddy*,
_name_table_add(name_table, pool, blk_ref)
blk_iter = blk_iter.Next
# 2. 遍历所有块
# 2. Walk all blocks
blk: rawptr[BasicBlock] = None
if blocks is not None:
blk = blocks.Head
while blk is not None:
# 检查终止指令
# Check terminator
if blk.IsTerminated == 0:
# 进一步检查最后一行是否为终止指令
# Also check whether the last line is a terminator
has_term: int = 0
line_check: rawptr[Line] = None
if blk.Lines is not None:
@@ -365,7 +348,7 @@ def verify_function(pool: memhub.MemBuddy*,
viperlib.snprintf(err_buf, 128, "ERROR: block '%s' missing terminator", blk_name)
_report_error(result, VERIFY_ERR_NO_TERMINATOR, err_buf)
# 遍历块内指令行
# Walk instruction lines in the block
seen_non_phi: int = 0
line: rawptr[Line] = None
if blk.Lines is not None:
@@ -375,7 +358,7 @@ def verify_function(pool: memhub.MemBuddy*,
if line.Buf is not None:
text: str = line.Buf
# 检查 phi 位置
# Check phi position
if _is_phi(text) == 1:
if seen_non_phi == 1:
phi_err: str = pool.alloc(128)
@@ -383,14 +366,14 @@ def verify_function(pool: memhub.MemBuddy*,
viperlib.snprintf(phi_err, 128, "ERROR: phi not at block start")
_report_error(result, VERIFY_ERR_PHI_NOT_FIRST, phi_err)
else:
# 非空行且非 phi 标记为已见非 phi
# Mark a non-empty non-phi line as seen-non-phi
if string.strlen(text) > 0:
seen_non_phi = 1
# 提取定义名
# Extract defined name
def_name: str = _extract_def_name(pool, text)
if def_name is not None:
# 检查重复定义
# Check for duplicate definition
dup: int = _name_table_add(name_table, pool, def_name)
if dup == 1:
dup_err: str = pool.alloc(128)
@@ -398,7 +381,7 @@ def verify_function(pool: memhub.MemBuddy*,
viperlib.snprintf(dup_err, 128, "ERROR: duplicate definition '%s'", def_name)
_report_error(result, VERIFY_ERR_DUPLICATE_DEF, dup_err)
# 检查未定义使用
# Check undefined uses
undef: int = _check_uses_in_line(name_table, text, def_name)
if undef > 0:
use_err: str = pool.alloc(128)
@@ -414,15 +397,14 @@ def verify_function(pool: memhub.MemBuddy*,
def verify_module(pool: memhub.MemBuddy*, mod: rawptr[LLVMModule], result: rawptr[VerifyResult]) -> int:
"""验证模块中所有函数的 SSA 正确性。
返回: 错误总数
"""Verify SSA correctness of all functions in the module.
Returns the total error count.
"""
if mod is None or result is None: return 1
func: rawptr[Function] = mod.FuncHead
while func is not None:
if func.IsDeclared == 0:
# 只验证有函数体的定义,跳过声明
# Verify only definitions with a body; skip declarations
verify_function(pool, func, result)
func = func.Next
return result.ErrorCount
+12 -12
View File
@@ -8,7 +8,7 @@ from openssl.types import *
# 含 BIO_ADDR/BIO_ADDRINFO:主机名解析(getaddrinfo 封装)+ TCP 连接/读写。
# ============================================================
# ---- BIO_METHOD 获取 ----
# ---- BIO_METHOD accessors ----
extern def BIO_s_mem() -> BioMethodPtr
extern def BIO_s_socket() -> BioMethodPtr
extern def BIO_s_connect() -> BioMethodPtr
@@ -18,7 +18,7 @@ extern def BIO_s_file() -> BioMethodPtr
extern def BIO_s_null() -> BioMethodPtr
extern def BIO_f_buffer() -> BioMethodPtr
# ---- 创建 / 释放 ----
# ---- Create / free ----
# BIO *BIO_new(const BIO_METHOD *type)
extern def BIO_new(type: BioMethodPtr) -> BioPtr
# BIO *BIO_new_socket(int sock, int close_flag)
@@ -38,7 +38,7 @@ extern def BIO_free(a: BioPtr) -> int
extern def BIO_free_all(a: BioPtr)
extern def BIO_vfree(a: BioPtr)
# ---- 读写 ----
# ---- Read / write ----
# int BIO_read(BIO *b, void *data, int dlen)
extern def BIO_read(b: BioPtr, data: bytes, dlen: int) -> int
# int BIO_write(BIO *b, const void *data, int dlen)
@@ -52,10 +52,10 @@ extern def BIO_ctrl_pending(b: BioPtr) -> sizet
# size_t BIO_ctrl_wpending(BIO *b)
extern def BIO_ctrl_wpending(b: BioPtr) -> sizet
# ---- BIO 控制(宏的底层 ctrl;连接/非阻塞等由它实现)----
# ---- BIO control (backing ctrl for macros; connect/nonblocking implemented here) ----
# long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg)
extern def BIO_ctrl(bp: BioPtr, cmd: int, larg: int32, parg: bytes) -> int32
# int BIO_do_connect(BIO *b) —— 宏:BIO_ctrl(b, BIO_C_DO_STATE_MACHINE, 0, NULL)
# int BIO_do_connect(BIO *b) -- macro: BIO_ctrl(b, BIO_C_DO_STATE_MACHINE, 0, NULL)
define BioCDoStateMachine = 101
define BioCSetConnect = 100
define BioCSetNbio = 102
@@ -63,12 +63,12 @@ define BioCSetFd = 104
define BioCGetFd = 105
define BioNoclose = 0x00
define BioClose = 0x01
# BIO 链
# BIO chain
# BIO *BIO_push(BIO *b, BIO *append) / BIO *BIO_pop(BIO *b)
extern def BIO_push(b: BioPtr, append: BioPtr) -> BioPtr
extern def BIO_pop(b: BioPtr) -> BioPtr
# ---- 便捷控制封装(对应 bio.h 宏,避免 Viper 端裸用 BIO_ctrl)----
# ---- Convenience control wrappers (match bio.h macros; avoid raw BIO_ctrl in Viper) ----
def bio_set_conn_hostname(b: BioPtr, name: str):
BIO_ctrl(b, BioCSetConnect, 0, name)
@@ -86,7 +86,7 @@ def bio_do_connect(b: BioPtr) -> int:
# ============================================================
# Socket 原语(BIO 层直接管 fd)
# Socket primitives (BIO manages fd directly)
# ============================================================
define BioSockReuseaddr = 0x01
define BioSockV6Only = 0x02
@@ -116,7 +116,7 @@ extern def BIO_set_tcp_ndelay(sock: int, turn_on: int) -> int
extern def BIO_sock_should_retry(i: int) -> int
# ============================================================
# BIO_ADDR / BIO_ADDRINFO(网络地址,TCP 解析用)
# BIO_ADDR / BIO_ADDRINFO (network addresses, for TCP resolution)
# ============================================================
# BIO_ADDR *BIO_ADDR_new(void) / dup
extern def BIO_ADDR_new() -> BioAddrPtr
@@ -128,14 +128,14 @@ extern def BIO_ADDR_family(ap: BioAddrPtr) -> int
extern def BIO_ADDR_rawport(ap: BioAddrPtr) -> uint16
# int BIO_ADDR_rawaddress(const BIO_ADDR *ap, void *p, size_t *l)
extern def BIO_ADDR_rawaddress(ap: BioAddrPtr, p: bytes, l: bytes) -> int
# char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric) → 返回需 CRYPTO_free
# char *BIO_ADDR_hostname_string(const BIO_ADDR *ap, int numeric) -> result must be CRYPTO_free'd
extern def BIO_ADDR_hostname_string(ap: BioAddrPtr, numeric: int) -> str
# char *BIO_ADDR_service_string(const BIO_ADDR *ap, int numeric)
extern def BIO_ADDR_service_string(ap: BioAddrPtr, numeric: int) -> str
# char *BIO_ADDR_path_string(const BIO_ADDR *ap)
extern def BIO_ADDR_path_string(ap: BioAddrPtr) -> str
# BIO_ADDRINFO 遍历
# BIO_ADDRINFO iteration
extern def BIO_ADDRINFO_free(bai: BioAddrInfoPtr)
# const BIO_ADDRINFO *BIO_ADDRINFO_next(const BIO_ADDRINFO *bai)
extern def BIO_ADDRINFO_next(bai: BioAddrInfoPtr) -> BioAddrInfoPtr
@@ -148,7 +148,7 @@ extern def BIO_ADDRINFO_protocol(bai: BioAddrInfoPtr) -> int
# const BIO_ADDR *BIO_ADDRINFO_address(const BIO_ADDRINFO *bai)
extern def BIO_ADDRINFO_address(bai: BioAddrInfoPtr) -> BioAddrPtr
# 主机名解析(getaddrinfo 封装)
# Hostname resolution (getaddrinfo wrapper)
define BioLookupClient = 0
define BioLookupServer = 1
# int BIO_lookup(const char *host, const char *service, int lookup_type,
+11 -11
View File
@@ -6,17 +6,17 @@ from openssl.types import *
# 对应头文件: crypto.h / opensslv.h
# ============================================================
# ---- 版本查询类型(OpenSSL_version(type) 的 type 取值)----
define OpenSslVersion = 0# OPENSSL_VERSION 完整版本文本
define OpenSslBuiltOn = 2# OPENSSL_BUILT_ON 编译时间
define OpenSslPlatform = 3# OPENSSL_PLATFORM 平台
# ---- Version query types (type arg of OpenSSL_version(type)) ----
define OpenSslVersion = 0# OPENSSL_VERSION full version text
define OpenSslBuiltOn = 2# OPENSSL_BUILT_ON build time
define OpenSslPlatform = 3# OPENSSL_PLATFORM platform
define OpenSslDir = 4# OPENSSL_DIR
define OpenSslEnginesDir = 5# OPENSSL_ENGINES_DIR
define OpenSslVersionString = 6# OPENSSL_VERSION_STRING "3.x.y"
define OpenSslModulesDir = 8# OPENSSL_MODULES_DIR
define OpenSslCpuInfo = 9# OPENSSL_CPU_INFO
# ---- OPENSSL_init_crypto / OPENSSL_init_ssl 选项位 ----
# ---- OPENSSL_init_crypto / OPENSSL_init_ssl option bits ----
define OpenSslInitNoLoadCryptoStrings = 0x00000001
define OpenSslInitNoLoadConfig = 0x00000004
define OpenSslInitNoAddAllCiphers = 0x00000008
@@ -28,19 +28,19 @@ define OpenSslInitAtFork = 0x00020000
define OpenSslInitNoLoadSslStrings = 0x00100000
define OpenSslInitNoAtomics = 0x00200000
# ---- 版本号(3.6.2,随链接库而定)----
# ---- Version numbers (3.6.2, depends on the linked library) ----
define OpenSslVersionMajor = 3
define OpenSslVersionMinor = 6
define OpenSslVersionPatch = 2
# ---- 全局初始化 / 清理 ----
# ---- Global init / cleanup ----
# int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
extern def OPENSSL_init_crypto(opts: uint64, settings: bytes) -> int
# int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
extern def OPENSSL_init_ssl(opts: uint64, settings: bytes) -> int
extern def OPENSSL_cleanup()
# ---- 版本查询 ----
# ---- Version query ----
# const char *OpenSSL_version(int type)
extern def OpenSSL_version(type: int) -> str
# unsigned long OpenSSL_version_num(void)
@@ -48,7 +48,7 @@ extern def OpenSSL_version_num() -> uint32
extern def OPENSSL_version_pre_release() -> str
extern def OPENSSL_version_build_metadata() -> str
# ---- 内存 / 安全擦除 ----
# ---- Memory / secure erase ----
# void OPENSSL_cleanse(void *ptr, size_t len)
extern def OPENSSL_cleanse(ptr: bytes, len: sizet)
# void CRYPTO_free(void *ptr, const char *file, int line)
@@ -57,7 +57,7 @@ extern def CRYPTO_free(ptr: bytes, file: str, line: int)
extern def CRYPTO_malloc(num: sizet, file: str, line: int) -> bytes
# int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len)
extern def CRYPTO_memcmp(in_a: bytes, in_b: bytes, len: sizet) -> int
# char *OPENSSL_buf2hexstr(const unsigned char *buf, long buflen) → 由 ossl_free 释放
# char *OPENSSL_buf2hexstr(const unsigned char *buf, long buflen) -> free with ossl_free
extern def OPENSSL_buf2hexstr(buf: bytes, buflen: int32) -> str
# int OPENSSL_hexchar2int(unsigned char c)
extern def OPENSSL_hexchar2int(c: uint8) -> int
@@ -66,7 +66,7 @@ extern def OPENSSL_strcasecmp(s1: str, s2: str) -> int
# int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t n)
extern def OPENSSL_strncasecmp(s1: str, s2: str, n: sizet) -> int
# ---- 便捷封装(OpenSSL 的 CRYPTO_free/CRYPTO_malloc 宏带 file/line 两参,此处封装为单参)----
# ---- Convenience wrappers (CRYPTO_free/CRYPTO_malloc macros take file/line; wrapped as single-arg) ----
def ossl_free(ptr: bytes):
CRYPTO_free(ptr, "", 0)
+7 -7
View File
@@ -6,7 +6,7 @@ from openssl.types import *
# 对应头文件: err.h
# ============================================================
# unsigned long ERR_get_error(void) — 弹出队列头并返回错误码
# unsigned long ERR_get_error(void) -- pop queue head and return error code
extern def ERR_get_error() -> uint32
# unsigned long ERR_peek_error(void)
extern def ERR_peek_error() -> uint32
@@ -15,7 +15,7 @@ extern def ERR_peek_last_error() -> uint32
# void ERR_clear_error(void)
extern def ERR_clear_error()
# char *ERR_error_string(unsigned long e, char *buf) — buf 可为 NULL(用内部静态缓冲)
# char *ERR_error_string(unsigned long e, char *buf) -- buf may be NULL (uses internal static buffer)
extern def ERR_error_string(e: uint32, buf: bytes) -> str
# void ERR_error_string_n(unsigned long e, char *buf, size_t len)
extern def ERR_error_string_n(e: uint32, buf: bytes, len: sizet)
@@ -26,17 +26,17 @@ extern def ERR_func_error_string(e: uint32) -> str
# const char *ERR_reason_error_string(unsigned long e)
extern def ERR_reason_error_string(e: uint32) -> str
# 栈标记(用于部分失败回滚)
# Stack marks (for partial-failure rollback)
extern def ERR_set_mark() -> int
extern def ERR_pop_to_mark() -> int
extern def ERR_clear_last_mark() -> int
# void ERR_print_errors(BIO *bp) — 把整个队列打到 BIO(见 bio.vp)
# void ERR_print_errors(BIO *bp) -- dump the whole queue to BIO (see bio.vp)
extern def ERR_print_errors(bp: BioPtr)
# void ERR_print_errors_fp(FILE *fp) — FILE* 暂不暴露
# void ERR_print_errors_fp(FILE *fp) -- FILE* not exposed yet
# ---- 错误码拆包(内联宏等价物)----
# OpenSSL 错误码布局:系统位 0x80000000,库 24-31,原因 0-15
# ---- Error code unpacking (inline-macro equivalents) ----
# OpenSSL error code layout: system bit 0x80000000, library 24-31, reason 0-15
define ErrLibMask = 0xff000000
define ErrReasonMask = 0x0000ffff
define ErrSystemFlag = 0x80000000
+16 -16
View File
@@ -15,7 +15,7 @@ define EvpMaxIvLength = 16
define EvpMaxBlockLength = 32
# ============================================================
# 1) 摘要算法对象(const EVP_MD*)
# 1) Digest algorithm objects (const EVP_MD*)
# ============================================================
extern def EVP_md5() -> EvpMdPtr
extern def EVP_sha1() -> EvpMdPtr
@@ -38,7 +38,7 @@ extern def EVP_MD_get_block_size(md: EvpMdPtr) -> int
extern def EVP_MD_get_type(md: EvpMdPtr) -> int
# ============================================================
# 2) 摘要上下文(增量计算)
# 2) Digest contexts (incremental)
# ============================================================
# EVP_MD_CTX *EVP_MD_CTX_new(void)
extern def EVP_MD_CTX_new() -> EvpMdCtxPtr
@@ -58,13 +58,13 @@ extern def EVP_DigestFinal_ex(ctx: EvpMdCtxPtr, md: bytes, s: bytes) -> int
extern def EVP_DigestFinal(ctx: EvpMdCtxPtr, md: bytes, s: bytes) -> int
# int EVP_MD_CTX_get_size(const EVP_MD_CTX *ctx)
extern def EVP_MD_CTX_get_size(ctx: EvpMdCtxPtr) -> int
# 一次性摘要: int EVP_Digest(const void *data, size_t count, unsigned char *md,
# One-shot digest: int EVP_Digest(const void *data, size_t count, unsigned char *md,
# unsigned int *size, const EVP_MD *type, ENGINE *impl)
extern def EVP_Digest(data: bytes, count: sizet, md: bytes, size: bytes,
type: EvpMdPtr, impl: bytes) -> int
# ============================================================
# 3) 对称加密算法对象(const EVP_CIPHER*)
# 3) Symmetric cipher objects (const EVP_CIPHER*)
# ============================================================
extern def EVP_aes_128_ecb() -> EvpCipherPtr
extern def EVP_aes_128_cbc() -> EvpCipherPtr
@@ -87,7 +87,7 @@ extern def EVP_chacha20() -> EvpCipherPtr
extern def EVP_rc4() -> EvpCipherPtr
# const EVP_CIPHER *EVP_get_cipherbyname(const char *name)
extern def EVP_get_cipherbyname(name: str) -> EvpCipherPtr
# 参数查询
# Parameter queries
extern def EVP_CIPHER_get_block_size(cipher: EvpCipherPtr) -> int
extern def EVP_CIPHER_get_key_length(cipher: EvpCipherPtr) -> int
extern def EVP_CIPHER_get_iv_length(cipher: EvpCipherPtr) -> int
@@ -95,7 +95,7 @@ extern def EVP_CIPHER_get_nid(cipher: EvpCipherPtr) -> int
extern def EVP_CIPHER_get_type(cipher: EvpCipherPtr) -> int
# ============================================================
# 4) 对称加解密上下文
# 4) Symmetric cipher contexts
# ============================================================
# EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void)
extern def EVP_CIPHER_CTX_new() -> EvpCipherCtxPtr
@@ -106,7 +106,7 @@ extern def EVP_CIPHER_CTX_reset(c: EvpCipherCtxPtr) -> int
# int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in)
extern def EVP_CIPHER_CTX_copy(out: EvpCipherCtxPtr, inn: EvpCipherCtxPtr) -> int
# 统一接口(enc: 1 加密 / 0 解密)
# Unified interface (enc: 1 encrypt / 0 decrypt)
# int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
# ENGINE *impl, const unsigned char *key, const unsigned char *iv, int enc)
extern def EVP_CipherInit_ex(ctx: EvpCipherCtxPtr, cipher: EvpCipherPtr,
@@ -122,7 +122,7 @@ extern def EVP_CipherFinal_ex(ctx: EvpCipherCtxPtr, outm: bytes, outl: bytes) ->
extern def EVP_CipherInit(ctx: EvpCipherCtxPtr, cipher: EvpCipherPtr,
key: bytes, iv: bytes, enc: int) -> int
# 分体加密/解密(对上面统一接口的细分,省略相同底层)
# Split encrypt/decrypt (specializations of the unified interface above)
# int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
# ENGINE *impl, const unsigned char *key, const unsigned char *iv)
extern def EVP_EncryptInit_ex(ctx: EvpCipherCtxPtr, cipher: EvpCipherPtr,
@@ -145,7 +145,7 @@ extern def EVP_DecryptUpdate(ctx: EvpCipherCtxPtr, out: bytes, outl: bytes,
# int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl)
extern def EVP_DecryptFinal_ex(ctx: EvpCipherCtxPtr, outm: bytes, outl: bytes) -> int
# 上下文查询/控制
# Context query / control
extern def EVP_CIPHER_CTX_set_padding(c: EvpCipherCtxPtr, pad: int) -> int
extern def EVP_CIPHER_CTX_set_key_length(c: EvpCipherCtxPtr, keylen: int) -> int
extern def EVP_CIPHER_CTX_ctrl(ctx: EvpCipherCtxPtr, type: int, arg: int, ptr: bytes) -> int
@@ -159,13 +159,13 @@ extern def EVP_CIPHER_CTX_get_iv(ctx: EvpCipherCtxPtr, iv: bytes, len: sizet) ->
# int EVP_CIPHER_CTX_set_iv(EVP_CIPHER_CTX *ctx, const void *iv, size_t len)
extern def EVP_CIPHER_CTX_set_iv(ctx: EvpCipherCtxPtr, iv: bytes, len: sizet) -> int
# ---- GCM 相关 ctrl 常量(EVP_CIPHER_CTX_ctrl 使用)----
# ---- GCM ctrl constants (used with EVP_CIPHER_CTX_ctrl) ----
define EvpCtrlGcmSetIvl = 0x9# EVP_CTRL_AEAD_SET_IVLEN
define EvpCtrlGcmGetTag = 0x10# EVP_CTRL_AEAD_GET_TAG
define EvpCtrlGcmSetTag = 0x11# EVP_CTRL_AEAD_SET_TAG
# ============================================================
# 5) Base64(EVP 编解码块函数)
# 5) Base64 (EVP encode/decode block functions)
# ============================================================
# int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int n)
extern def EVP_EncodeBlock(t: bytes, f: bytes, n: int) -> int
@@ -173,12 +173,12 @@ extern def EVP_EncodeBlock(t: bytes, f: bytes, n: int) -> int
extern def EVP_DecodeBlock(t: bytes, f: bytes, n: int) -> int
# ============================================================
# 6) 非对称(EVP_PKEY / EVP_PKEY_CTX)
# 6) Asymmetric (EVP_PKEY / EVP_PKEY_CTX)
# ============================================================
# EVP_PKEY *EVP_PKEY_new(void) / void EVP_PKEY_free(EVP_PKEY *pkey)
extern def EVP_PKEY_new() -> EvpPkeyPtr
extern def EVP_PKEY_free(pkey: EvpPkeyPtr)
# int EVP_PKEY_get_size(const EVP_PKEY *pkey) (签名长度/密文长度)
# int EVP_PKEY_get_size(const EVP_PKEY *pkey) (signature/ciphertext length)
extern def EVP_PKEY_get_size(pkey: EvpPkeyPtr) -> int
extern def EVP_PKEY_get_id(pkey: EvpPkeyPtr) -> int
extern def EVP_PKEY_get_base_id(pkey: EvpPkeyPtr) -> int
@@ -190,7 +190,7 @@ extern def EVP_PKEY_CTX_new(pkey: EvpPkeyPtr, e: bytes) -> EvpPkeyCtxPtr
# void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx)
extern def EVP_PKEY_CTX_free(ctx: EvpPkeyCtxPtr)
# 签名 / 验签(裸 RSA/EC)
# Sign / verify (raw RSA/EC)
extern def EVP_PKEY_sign_init(ctx: EvpPkeyCtxPtr) -> int
extern def EVP_PKEY_verify_init(ctx: EvpPkeyCtxPtr) -> int
extern def EVP_PKEY_encrypt_init(ctx: EvpPkeyCtxPtr) -> int
@@ -206,7 +206,7 @@ extern def EVP_PKEY_encrypt(ctx: EvpPkeyCtxPtr, out: bytes, outlen: bytes,
extern def EVP_PKEY_decrypt(ctx: EvpPkeyCtxPtr, out: bytes, outlen: bytes,
inn: bytes, inlen: sizet) -> int
# 摘要签名/验签(DigestSign*:RSA-PSS/ECDSA 最常用)
# Digest sign/verify (DigestSign*: mostly RSA-PSS/ECDSA)
# int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type,
# ENGINE *e, EVP_PKEY *pkey)
extern def EVP_DigestSignInit(ctx: EvpMdCtxPtr, pctx: bytes, type: EvpMdPtr,
@@ -222,7 +222,7 @@ extern def EVP_DigestVerifyUpdate(ctx: EvpMdCtxPtr, data: bytes, dsize: sizet) -
extern def EVP_DigestVerifyFinal(ctx: EvpMdCtxPtr, sig: bytes, siglen: sizet) -> int
# ============================================================
# 7) 口令派生 / 辅助
# 7) Key derivation / helpers
# ============================================================
# int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
# const unsigned char *salt, const unsigned char *data, int datal, int count,
+2 -2
View File
@@ -6,13 +6,13 @@ from openssl.types import *
# 对应头文件: hmac.h
# ============================================================
# ---- 一次性 HMAC ----
# ---- One-shot HMAC ----
# unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len,
# const unsigned char *d, size_t n, unsigned char *md, unsigned int *md_len)
extern def HMAC(evp_md: EvpMdPtr, key: bytes, key_len: int,
d: bytes, n: sizet, md: bytes, md_len: bytes) -> bytes
# ---- 上下文增量 HMAC(OpenSSL 3.x 提供)----
# ---- Incremental HMAC context (provided by OpenSSL 3.x) ----
# HMAC_CTX *HMAC_CTX_new(void)
extern def HMAC_CTX_new() -> HmacCtxPtr
# void HMAC_CTX_free(HMAC_CTX *ctx)
+2 -2
View File
@@ -6,7 +6,7 @@ from openssl.types import *
# 对应头文件: objects.h / obj_mac.h
# ============================================================
# ---- 常用 NID(obj_mac.h 常量)----
# ---- Common NIDs (obj_mac.h constants) ----
define NidCommonName = 13
define NidCountryName = 14
define NidLocalityName = 15
@@ -30,7 +30,7 @@ define NidMd5 = 4
define NidHmac = 855
define NidX9_62IdEcPublicKey = 408
# ---- OBJ / NID 转换 ----
# ---- OBJ / NID conversion ----
# int OBJ_txt2nid(const char *s)
extern def OBJ_txt2nid(s: str) -> int
# int OBJ_sn2nid(const char *s) / int OBJ_ln2nid(const char *s)
+5 -5
View File
@@ -9,7 +9,7 @@ from openssl.types import *
# (函数返回新对象)。回调 cb 用 bytes(Viper 传 t.CPtr(0))。
# ============================================================
# ---- X509 证书 ----
# ---- X509 certificate ----
# X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb *cb, void *u)
extern def PEM_read_bio_X509(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X509Ptr
# int PEM_write_bio_X509(BIO *bp, X509 *x)
@@ -19,7 +19,7 @@ extern def PEM_read_bio_X509_AUX(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X
# int PEM_write_bio_X509_AUX(BIO *bp, X509 *x)
extern def PEM_write_bio_X509_AUX(bp: BioPtr, x: X509Ptr) -> int
# ---- 私钥 / 公钥(EVP_PKEY 通用容器)----
# ---- Private / public key (generic EVP_PKEY container) ----
# EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u)
extern def PEM_read_bio_PrivateKey(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> EvpPkeyPtr
# int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
@@ -31,7 +31,7 @@ extern def PEM_read_bio_PUBKEY(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> Evp
# int PEM_write_bio_PUBKEY(BIO *bp, EVP_PKEY *x)
extern def PEM_write_bio_PUBKEY(bp: BioPtr, x: EvpPkeyPtr) -> int
# ---- 传统 RSA 私钥(OpenSSL 3.x 仍导出,兼容旧文件)----
# ---- Legacy RSA private key (still exported in OpenSSL 3.x, for old files) ----
# RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x, pem_password_cb *cb, void *u)
extern def PEM_read_bio_RSAPrivateKey(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> bytes
# int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc,
@@ -39,13 +39,13 @@ extern def PEM_read_bio_RSAPrivateKey(bp: BioPtr, x: bytes, cb: bytes, u: bytes)
extern def PEM_write_bio_RSAPrivateKey(bp: BioPtr, x: bytes, enc: EvpCipherPtr,
kstr: bytes, klen: int, cb: bytes, u: bytes) -> int
# ---- 证书请求 / CRL ----
# ---- Certificate request / CRL ----
# X509_REQ *PEM_read_bio_X509_REQ(BIO *bp, X509_REQ **x, pem_password_cb *cb, void *u)
extern def PEM_read_bio_X509_REQ(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X509ReqPtr
# X509_CRL *PEM_read_bio_X509_CRL(BIO *bp, X509_CRL **x, pem_password_cb *cb, void *u)
extern def PEM_read_bio_X509_CRL(bp: BioPtr, x: bytes, cb: bytes, u: bytes) -> X509CrlPtr
# ---- 便捷读取封装(调用方无需传递 x 出参/cb)----
# ---- Convenience readers (caller passes no x out-param / cb) ----
def pem_read_bio_x509_simple(bp: BioPtr) -> X509Ptr:
return PEM_read_bio_X509(bp, None, None, None)
+2 -2
View File
@@ -6,9 +6,9 @@ from openssl.types import *
# 对应头文件: rand.h
# ============================================================
# int RAND_bytes(unsigned char *buf, int num) — 加密安全随机
# int RAND_bytes(unsigned char *buf, int num) -- cryptographically secure random
extern def RAND_bytes(buf: bytes, num: int) -> int
# int RAND_priv_bytes(unsigned char *buf, int num) — 私钥级随机(私钥/密钥建议用)
# int RAND_priv_bytes(unsigned char *buf, int num) -- private-key-grade random (preferred for keys)
extern def RAND_priv_bytes(buf: bytes, num: int) -> int
# int RAND_status(void)
extern def RAND_status() -> int
+1 -1
View File
@@ -25,4 +25,4 @@ extern def SHA256(d: bytes, n: sizet, md: bytes) -> bytes
extern def SHA384(d: bytes, n: sizet, md: bytes) -> bytes
# unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md)
extern def SHA512(d: bytes, n: sizet, md: bytes) -> bytes
# SHA-3 只经 EVP 提供(EVP_sha3_256() 等,见 evp.vp)
# SHA-3 is provided only via EVP (EVP_sha3_256(), etc.; see evp.vp)
+22 -22
View File
@@ -16,26 +16,26 @@ from openssl.types import *
# SSL_write/SSL_read
# ============================================================
# ---- 证书文件格式(SSL_CTX_use_*_file / use_certificate_file 的 type)----
# ---- Certificate file format (type arg of SSL_CTX_use_*_file / use_certificate_file) ----
define X509FiletypePem = 1
define X509FiletypeAsn1 = 2
# SSL_FILETYPE_* 与 X509_FILETYPE_* 同值
# SSL_FILETYPE_* shares the same value as X509_FILETYPE_*
define SslFiletypePem = 1
define SslFiletypeAsn1 = 2
# ---- 校验模式(SSL_CTX_set_verify mode)----
# ---- Verify modes (SSL_CTX_set_verify mode) ----
define SslVerifyNone = 0x00
define SslVerifyPeer = 0x01
define SslVerifyFailIfNoPeerCert = 0x02
define SslVerifyClientOnce = 0x04
# ---- 协议版本(SSL_CTX_ctrl SSL_CTRL_SET_MIN/MAX_PROTO_VERSION 的 arg)----
# ---- Protocol versions (arg of SSL_CTRL_SET_MIN/MAX_PROTO_VERSION) ----
define Tls1Version = 0x0301
define Tls1_1Version = 0x0302
define Tls1_2Version = 0x0303
define Tls1_3Version = 0x0304
# ---- ctrl 命令 ----
# ---- ctrl commands ----
define SslCtrlSetTlsextHostname = 55# SSL_ctrl(ssl, ..., TLSEXT_NAMETYPE_host_name, (void*)name)
define SslCtrlSetMinProtoVersion = 123
define SslCtrlSetMaxProtoVersion = 124
@@ -43,11 +43,11 @@ define SslCtrlMode = 33
define SslCtrlClearMode = 34
define TlsextNametypeHostName = 0
# ---- shutdown 状态 ----
# ---- shutdown states ----
define SslSentShutdown = 1
define SslReceivedShutdown = 2
# ---- SSL_get_error 返回码 ----
# ---- SSL_get_error return codes ----
define SslErrorNone = 0
define SslErrorSsl = 1
define SslErrorWantRead = 2
@@ -62,7 +62,7 @@ define SslErrorWantAsyncJob = 10
define SslErrorWantClientHelloCb = 11
define SslErrorWantRetryVerify = 12
# ---- SSL_OP_* 选项 ----
# ---- SSL_OP_* options ----
# SSL_CTX_set_options(ctx, uint64_t)
define SslOpNoSslv2 = 0x01000000
define SslOpNoSslv3 = 0x02000000
@@ -73,9 +73,9 @@ define SslOpNoTlsv1_3 = 0x20000000
define SslOpAll = 0x00000fff
# ============================================================
# 初始化 / 方法
# Init / methods
# ============================================================
# (OPENSSL_init_ssl 见 crypto.vp)
# (see crypto.vp for OPENSSL_init_ssl)
# const SSL_METHOD *TLS_method(void) / *_client_method / *_server_method
extern def TLS_method() -> SslMethodPtr
extern def TLS_client_method() -> SslMethodPtr
@@ -90,7 +90,7 @@ extern def SSL_CTX_new(meth: SslMethodPtr) -> SslCtxPtr
extern def SSL_CTX_free(ctx: SslCtxPtr)
# int SSL_CTX_up_ref(SSL_CTX *ctx)
extern def SSL_CTX_up_ref(ctx: SslCtxPtr) -> int
# 证书 / 私钥
# Certificate / private key
# int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type)
extern def SSL_CTX_use_certificate_file(ctx: SslCtxPtr, file: str, type: int) -> int
# int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type)
@@ -99,7 +99,7 @@ extern def SSL_CTX_use_PrivateKey_file(ctx: SslCtxPtr, file: str, type: int) ->
extern def SSL_CTX_use_certificate_chain_file(ctx: SslCtxPtr, file: str) -> int
# int SSL_CTX_check_private_key(const SSL_CTX *ctx)
extern def SSL_CTX_check_private_key(ctx: SslCtxPtr) -> int
# 校验配置
# Verify config
# void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback)
extern def SSL_CTX_set_verify(ctx: SslCtxPtr, mode: int, callback: bytes)
# void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth)
@@ -118,11 +118,11 @@ extern def SSL_CTX_set_alpn_protos(ctx: SslCtxPtr, protos: bytes, protos_len: ui
extern def SSL_CTX_set_options(ctx: SslCtxPtr, op: uint64) -> uint64
# long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg)
extern def SSL_CTX_ctrl(ctx: SslCtxPtr, cmd: int, larg: int32, parg: bytes) -> int32
# long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) -- 用 ctrl? 不是,是函数
# long SSL_CTX_set_timeout(SSL_CTX *ctx, long t)
# int SSL_CTX_set_session_cache_mode(SSL_CTX *ctx, int mode)
# ============================================================
# SSL 对象
# SSL object
# ============================================================
# SSL *SSL_new(SSL_CTX *ctx)
extern def SSL_new(ctx: SslCtxPtr) -> SslPtr
@@ -136,7 +136,7 @@ extern def SSL_get_fd(ssl: SslPtr) -> int
# void SSL_set_connect_state(SSL *ssl) / accept
extern def SSL_set_connect_state(ssl: SslPtr)
extern def SSL_set_accept_state(ssl: SslPtr)
# 握手
# Handshake
extern def SSL_connect(ssl: SslPtr) -> int
extern def SSL_accept(ssl: SslPtr) -> int
# int SSL_do_handshake(SSL *ssl)
@@ -163,31 +163,31 @@ extern def SSL_get_verify_mode(ssl: SslPtr) -> int
# X509 *SSL_get_peer_certificate(const SSL *s) / SSL_get1_peer_certificate
extern def SSL_get_peer_certificate(s: SslPtr) -> X509Ptr
extern def SSL_get1_peer_certificate(s: SslPtr) -> X509Ptr
# 当前加密套件
# SSL_get_cipher_name 在 ssl.h 是宏(SSL_CIPHER_get_name(SSL_get_current_cipher(s))),
# FFI 拆成两个真实导出函数调用:
# Current cipher
# SSL_get_cipher_name is a macro in ssl.h (SSL_CIPHER_get_name(SSL_get_current_cipher(s)));
# FFI calls the two real exported functions instead.
# const SSL_CIPHER *SSL_get_current_cipher(const SSL *s)
extern def SSL_get_current_cipher(s: SslPtr) -> SslCipherPtr
# const char *SSL_CIPHER_get_name(const SSL_CIPHER *c)
extern def SSL_CIPHER_get_name(cipher: SslCipherPtr) -> str
# SNI: int SSL_set_tlsext_host_name(SSL *s, const char *name)(宏 → SSL_ctrl)
# SNI: int SSL_set_tlsext_host_name(SSL *s, const char *name) (macro -> SSL_ctrl)
# const char *SSL_get_servername(const SSL *s, int type)
extern def SSL_get_servername(s: SslPtr, type: int) -> str
# ALPN
# void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len)
extern def SSL_get0_alpn_selected(ssl: SslPtr, data: bytes, len: bytes)
# 控制 / 选项
# Control / options
# long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg)
extern def SSL_ctrl(ssl: SslPtr, cmd: int, larg: int32, parg: bytes) -> int32
# uint64_t SSL_set_options(SSL *ssl, uint64_t op)
extern def SSL_set_options(ssl: SslPtr, op: uint64) -> uint64
# int SSL_set1_host(SSL *s, const char *hostname) (主机名校验)
# int SSL_set1_host(SSL *s, const char *hostname) (hostname check)
extern def SSL_set1_host(s: SslPtr, hostname: str) -> int
# void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio)
extern def SSL_set_bio(s: SslPtr, rbio: BioPtr, wbio: BioPtr)
# ============================================================
# 便捷封装
# Convenience wrappers
# ============================================================
def ssl_set_tlsext_host_name(ssl: SslPtr, name: str) -> int:
return SSL_ctrl(ssl, SslCtrlSetTlsextHostname, TlsextNametypeHostName, name)
+4 -4
View File
@@ -14,18 +14,18 @@ import t, c
# opt -passes=asan 会报 invalid type for function argument。
# ============================================================
# ---- 基础上下文 ----
# ---- Base contexts ----
OsslLibCtxPtr: t.CTypedef = t.CPtr # OSSL_LIB_CTX*
EnginePtr: t.CTypedef = t.CPtr # ENGINE*
OpensslInitSettingsPtr: t.CTypedef = t.CPtr # OPENSSL_INIT_SETTINGS*
# ---- BIO(内存/文件/Socket/Connect)----
# ---- BIO (memory / file / socket / connect) ----
BioPtr: t.CTypedef = t.CPtr # BIO*
BioMethodPtr: t.CTypedef = t.CPtr # BIO_METHOD*
BioAddrPtr: t.CTypedef = t.CPtr # BIO_ADDR*
BioAddrInfoPtr: t.CTypedef = t.CPtr # BIO_ADDRINFO*
# ---- EVP(摘要/对称/非对称)----
# ---- EVP (digest / symmetric / asymmetric) ----
EvpMdPtr: t.CTypedef = t.CPtr # EVP_MD*
EvpMdCtxPtr: t.CTypedef = t.CPtr # EVP_MD_CTX*
EvpCipherPtr: t.CTypedef = t.CPtr # EVP_CIPHER*
@@ -35,7 +35,7 @@ EvpPkeyCtxPtr: t.CTypedef = t.CPtr # EVP_PKEY_CTX*
EvpEncodeCtxPtr: t.CTypedef = t.CPtr # EVP_ENCODE_CTX*
HmacCtxPtr: t.CTypedef = t.CPtr # HMAC_CTX*
# ---- X509 / 证书 ----
# ---- X509 / certificate ----
X509Ptr: t.CTypedef = t.CPtr # X509*
X509NamePtr: t.CTypedef = t.CPtr # X509_NAME*
X509NameEntryPtr: t.CTypedef = t.CPtr # X509_NAME_ENTRY*
+14 -14
View File
@@ -14,18 +14,18 @@ define X509Version2 = 1
define X509Version3 = 2
# ============================================================
# 1) 证书对象生命周期
# 1) Certificate object lifecycle
# ============================================================
# X509 *X509_new(void) / void X509_free(X509 *a)
extern def X509_new() -> X509Ptr
extern def X509_free(a: X509Ptr)
# int i2d_X509(X509 *a, unsigned char **pp) / X509 *d2i_X509(X509 **a, const unsigned char **pp, long length)
# (pp 语义复杂,Viper 端建议用 d2i_X509_bio/i2d_X509_bio,故不暴露裸 d2i/i2d)
# (pp semantics are complex; use d2i_X509_bio/i2d_X509_bio, so raw d2i/i2d are not exposed)
extern def d2i_X509_bio(bp: BioPtr, x509: bytes) -> X509Ptr
extern def i2d_X509_bio(bp: BioPtr, x509: X509Ptr) -> int
# ============================================================
# 2) 证书字段读取
# 2) Certificate field access
# ============================================================
# long X509_get_version(const X509 *x)
extern def X509_get_version(x: X509Ptr) -> int32
@@ -51,7 +51,7 @@ extern def X509_digest(data: X509Ptr, type: EvpMdPtr, md: bytes, len: bytes) ->
extern def X509_verify(a: X509Ptr, r: EvpPkeyPtr) -> int
# ============================================================
# 3) X509_NAME 读取(subject/issuer 各 RDN 段)
# 3) X509_NAME access (subject/issuer RDN entries)
# ============================================================
# int X509_NAME_entry_count(const X509_NAME *name)
extern def X509_NAME_entry_count(name: X509NamePtr) -> int
@@ -65,11 +65,11 @@ extern def X509_NAME_ENTRY_get_object(ne: X509NameEntryPtr) -> Asn1ObjectPtr
extern def X509_NAME_ENTRY_get_data(ne: X509NameEntryPtr) -> Asn1StringPtr
# int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid, char *buf, int len)
extern def X509_NAME_get_text_by_NID(name: X509NamePtr, nid: int, buf: bytes, len: int) -> int
# char *X509_NAME_oneline(const X509_NAME *a, char *buf, int size) → 返回指向 buf 的指针
# char *X509_NAME_oneline(const X509_NAME *a, char *buf, int size) -> returns pointer to buf
extern def X509_NAME_oneline(a: X509NamePtr, buf: bytes, size: int) -> str
# ============================================================
# 4) 扩展(SAN 等)
# 4) Extensions (SAN, ...)
# ============================================================
# int X509_get_ext_count(const X509 *x)
extern def X509_get_ext_count(x: X509Ptr) -> int
@@ -87,30 +87,30 @@ extern def X509_EXTENSION_get_data(ne: X509ExtensionPtr) -> Asn1OctetStringPtr
extern def X509V3_EXT_print(out: BioPtr, ext: X509ExtensionPtr,
flag: uint32, indent: int) -> int
# GENERAL_NAME 遍历(SAN:DNS/URI/IP/email)
# GENERAL_NAME iteration (SAN: DNS/URI/IP/email)
extern def GENERAL_NAME_free(a: GeneralNamePtr)
extern def GENERAL_NAMES_free(a: GeneralNamesPtr)
# int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen)
extern def GENERAL_NAME_print(out: BioPtr, gen: GeneralNamePtr) -> int
# 通用 OPENSSL 栈访问(STACK_OF 的内核,宏 sk_xxx_num/value 最终落到这)
# Generic OPENSSL stack access (backing STACK_OF; sk_xxx_num/value macros land here)
# int OPENSSL_sk_num(const OPENSSL_STACK *st)
extern def OPENSSL_sk_num(st: bytes) -> int
# void *OPENSSL_sk_value(const OPENSSL_STACK *st, int i)
extern def OPENSSL_sk_value(st: bytes, i: int) -> GeneralNamePtr
# void OPENSSL_sk_free(OPENSSL_STACK *st)
extern def OPENSSL_sk_free(st: bytes)
# const char *OBJ_nid2sn ... 已放 objects.vp;此处放 GNR 常量
# const char *OBJ_nid2sn ... lives in objects.vp; GNR constants go here
define GenEmail = 1
define GenDns = 2
define GenUri = 6
define GenIpadd = 7
# ---- SAN 便捷读取(把某类 GENERAL_NAME 数量统计)----
# ---- SAN convenience access (count GENERAL_NAME entries) ----
def x509_san_count(gns: GeneralNamesPtr) -> int:
return OPENSSL_sk_num(gns)
# ============================================================
# 5) 证书链校验(X509_STORE_CTX)
# 5) Certificate chain verification (X509_STORE_CTX)
# ============================================================
# X509_STORE *X509_STORE_new(void) / void X509_STORE_free(X509_STORE *v)
extern def X509_STORE_new() -> X509StorePtr
@@ -149,7 +149,7 @@ extern def X509_STORE_CTX_get_current_cert(ctx: X509StoreCtxPtr) -> X509Ptr
extern def X509_verify_cert_error_string(n: int32) -> str
# ============================================================
# 6) ASN.1 字符串 / 时间助手(asn1.h)
# 6) ASN.1 string / time helpers (asn1.h)
# ============================================================
# int ASN1_STRING_length(const ASN1_STRING *x)
extern def ASN1_STRING_length(x: Asn1StringPtr) -> int
@@ -157,7 +157,7 @@ extern def ASN1_STRING_length(x: Asn1StringPtr) -> int
extern def ASN1_STRING_get0_data(x: Asn1StringPtr) -> bytes
# int ASN1_STRING_type(const ASN1_STRING *x)
extern def ASN1_STRING_type(x: Asn1StringPtr) -> int
# int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in) → 返回长度,需 CRYPTO_free(*out)
# int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in) -> returns length; caller must CRYPTO_free(*out)
extern def ASN1_STRING_to_UTF8(out: bytes, inn: Asn1StringPtr) -> int
# int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
extern def ASN1_STRING_print(bp: BioPtr, v: Asn1StringPtr) -> int
@@ -171,7 +171,7 @@ extern def ASN1_TIME_compare(a: Asn1TimePtr, b: Asn1TimePtr) -> int
extern def ASN1_INTEGER_get(a: Asn1IntegerPtr) -> int32
# int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)
extern def ASN1_INTEGER_set(a: Asn1IntegerPtr, v: int32) -> int
# int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a) → 十进制打印到 BIO
# int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a) -> prints decimal to BIO
extern def i2a_ASN1_INTEGER(bp: BioPtr, a: Asn1IntegerPtr) -> int
# const char *ASN1_tag2str(int tag)
extern def ASN1_tag2str(tag: int) -> str
+5 -9
View File
@@ -111,24 +111,20 @@ def chmod(path: str, mode: int) -> int:
# =============================================================================
def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
"""列举目录下的文件/子目录名。
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
文件名通过 _mbuddy 分配内存。
max_count 为 out_names 数组最大容量。
返回实际列举的条目数。失败返回 0。"""
"""List file/subdirectory names under a directory.
out_names is a caller-provided char** array (each entry points to a filename allocated via _mbuddy); max_count is its capacity. Return the count, or 0 on failure."""
if platmacro.IS_WINDOWS:
return os._win32.listdir(path, out_names, max_count)
return os._posix.listdir(path, out_names, max_count)
# =============================================================================
# 命令执行
# Command Execution
# =============================================================================
def system(cmd: str) -> int:
"""执行命令行命令(通过 C 运行时 system())。
命令输出直接到终端(继承父进程 stdout/stderr)。
返回命令的退出码(0 表示成功)。"""
"""Execute a command line command (via the C runtime system()).
Output goes to the terminal (inherits parent stdout/stderr). Return the exit code (0 = success)."""
if platmacro.IS_WINDOWS:
return os._win32.system(cmd)
return os._posix.system_impl(cmd)
+28 -32
View File
@@ -5,20 +5,20 @@ import memhub
import stdlib
# POSIX 路径最大长度
# Maximum POSIX path length
define MAX_PATH_LEN = 4096
# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化)
# Module-level global MBuddy pointer (initialized by the user in main)
_mbuddy: memhub.MemBuddy*
# =============================================================================
# 文件/目录属性查询
# File/Directory Attribute Query
# =============================================================================
def _stat(path: str) -> rawptr[posix.Stat]:
"""调用 stat 并返回 Stat 结构体指针。失败返回 None。
注意:Stat 结构体通过 _mbuddy 分配,调用者负责释放。"""
"""Call stat and return a Stat struct pointer. Return None on failure.
Note: the Stat struct is allocated via _mbuddy; the caller must free it."""
st: posix.Stat = posix.Stat()
result: INT = posix.stat(path, t.CPtr(&st))
if result != 0:
@@ -27,7 +27,7 @@ def _stat(path: str) -> rawptr[posix.Stat]:
def exists(path: str) -> bool:
"""检查文件/目录是否存在"""
"""Check whether the file/directory exists"""
result: INT = posix.access(path, posix.F_OK)
if result == 0:
return True
@@ -35,7 +35,7 @@ def exists(path: str) -> bool:
def isdir(path: str) -> bool:
"""检查路径是否为目录"""
"""Check whether the path is a directory"""
st: posix.Stat = posix.Stat()
result: INT = posix.stat(path, t.CPtr(&st))
if result != 0:
@@ -46,7 +46,7 @@ def isdir(path: str) -> bool:
def isfile(path: str) -> bool:
"""检查路径是否为普通文件"""
"""Check whether the path is a regular file"""
st: posix.Stat = posix.Stat()
result: INT = posix.stat(path, t.CPtr(&st))
if result != 0:
@@ -57,7 +57,7 @@ def isfile(path: str) -> bool:
def getsize(path: str) -> int64:
"""返回文件大小(字节)。失败返回 -1。"""
"""Return the file size in bytes. Return -1 on failure."""
st: posix.Stat = posix.Stat()
result: INT = posix.stat(path, t.CPtr(&st))
if result != 0:
@@ -66,11 +66,11 @@ def getsize(path: str) -> int64:
# =============================================================================
# 工作目录
# Working Directory
# =============================================================================
def getcwd_impl() -> str:
"""返回当前工作目录(通过 _mbuddy 分配内存)"""
"""Return the current working directory (memory allocated via _mbuddy)"""
buf: bytes = _mbuddy.alloc(MAX_PATH_LEN)
if buf == None: return None
result: CHARPTR = posix.getcwd(buf, MAX_PATH_LEN)
@@ -80,7 +80,7 @@ def getcwd_impl() -> str:
def chdir_impl(path: str) -> int:
"""切换当前工作目录。成功返回 0,失败返回 -1。"""
"""Switch the current working directory. Return 0 on success, -1 on failure."""
result: INT = posix.chdir(path)
if result != 0:
return -1
@@ -88,12 +88,12 @@ def chdir_impl(path: str) -> int:
# =============================================================================
# 文件/目录操作
# File/Directory Operations
# =============================================================================
def mkdir_impl(path: str) -> int:
"""创建目录。成功返回 0,失败返回 -1。
默认权限 0o755(rwxr-xr-x)。"""
"""Create a directory. Return 0 on success, -1 on failure.
Default permissions are 0o755 (rwxr-xr-x)."""
result: INT = posix.mkdir(path, 0o755)
if result != 0:
return -1
@@ -101,7 +101,7 @@ def mkdir_impl(path: str) -> int:
def remove(path: str) -> int:
"""删除文件。成功返回 0,失败返回 -1。"""
"""Delete a file. Return 0 on success, -1 on failure."""
result: INT = posix.unlink(path)
if result != 0:
return -1
@@ -109,7 +109,7 @@ def remove(path: str) -> int:
def rmdir_impl(path: str) -> int:
"""删除空目录。成功返回 0,失败返回 -1。"""
"""Delete an empty directory. Return 0 on success, -1 on failure."""
result: INT = posix.rmdir(path)
if result != 0:
return -1
@@ -117,7 +117,7 @@ def rmdir_impl(path: str) -> int:
def rename_impl(old_path: str, new_path: str) -> int:
"""重命名/移动文件或目录。成功返回 0,失败返回 -1。"""
"""Rename/move a file or directory. Return 0 on success, -1 on failure."""
result: INT = posix.rename(old_path, new_path)
if result != 0:
return -1
@@ -125,8 +125,8 @@ def rename_impl(old_path: str, new_path: str) -> int:
def chmod_impl(path: str, mode: int) -> int:
"""修改文件权限。成功返回 0,失败返回 -1。
mode 为 POSIX 权限位(如 0o755)。"""
"""Modify file permissions. Return 0 on success, -1 on failure.
mode is POSIX permission bits (e.g. 0o755)."""
result: INT = posix.chmod(path, posix.mode_t(mode))
if result != 0:
return -1
@@ -134,15 +134,12 @@ def chmod_impl(path: str, mode: int) -> int:
# =============================================================================
# 目录列举(opendir / readdir / closedir)
# Directory Listing (opendir / readdir / closedir)
# =============================================================================
def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
"""列举目录下的文件/子目录名。
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
文件名通过 _mbuddy 分配内存。
max_count 为 out_names 数组最大容量。
返回实际列举的条目数。失败返回 0。"""
"""List file/subdirectory names under a directory.
out_names is a caller-provided char** array (each entry points to a filename allocated via _mbuddy); max_count is its capacity. Return the count, or 0 on failure."""
dirp: posix.DIR = posix.opendir(path)
if dirp == None:
return 0
@@ -153,7 +150,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
if entry == None:
break
# 读取 d_name 字段
# Read the d_name field
name: str = entry.d_name
if name[0] == '.':
if name[1] == 0:
@@ -163,7 +160,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
# ".."
continue
# 复制文件名到新分配的内存
# Copy the filename into newly allocated memory
name_len: sizet = len(name)
name_buf: bytes = _mbuddy.alloc(name_len + 1)
if name_buf == None: break
@@ -180,11 +177,10 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
# =============================================================================
# 命令执行
# Command Execution
# =============================================================================
def system_impl(cmd: str) -> int:
"""执行命令行命令(通过 C 运行时 system())。
命令输出直接到子进程的终端(继承父进程 stdout/stderr)。
返回命令的退出码(0 表示成功)。"""
"""Execute a command line command (via the C runtime system()).
Output goes to the child terminal (inherits parent stdout/stderr). Return the exit code (0 = success)."""
return stdlib.system(cmd)
+32 -37
View File
@@ -6,27 +6,27 @@ import memhub
import stdlib
# Windows 路径最大长度
# Maximum Windows path length
define MAX_PATH_LEN = 260
# INVALID_FILE_ATTRIBUTES
define INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF
# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化)
# Module-level global MBuddy pointer (initialized by the user in main)
_mbuddy: memhub.MemBuddy*
# =============================================================================
# 文件/目录属性查询
# File/Directory Attribute Query
# =============================================================================
def _get_attrs(path: str) -> ULONG:
"""获取文件属性。失败返回 INVALID_FILE_ATTRIBUTES。"""
"""Get file attributes. Return INVALID_FILE_ATTRIBUTES on failure."""
return w32.win32file.GetFileAttributesA(path)
def exists(path: str) -> bool:
"""检查文件/目录是否存在"""
"""Check whether the file/directory exists"""
attrs: ULONG = _get_attrs(path)
if attrs == INVALID_FILE_ATTRIBUTES:
return False
@@ -34,7 +34,7 @@ def exists(path: str) -> bool:
def isdir(path: str) -> bool:
"""检查路径是否为目录"""
"""Check whether the path is a directory"""
attrs: ULONG = _get_attrs(path)
if attrs == INVALID_FILE_ATTRIBUTES:
return False
@@ -44,7 +44,7 @@ def isdir(path: str) -> bool:
def isfile(path: str) -> bool:
"""检查路径是否为普通文件"""
"""Check whether the path is a regular file"""
attrs: ULONG = _get_attrs(path)
if attrs == INVALID_FILE_ATTRIBUTES:
return False
@@ -54,7 +54,7 @@ def isfile(path: str) -> bool:
def getsize(path: str) -> int64:
"""返回文件大小(字节)。失败返回 -1。"""
"""Return the file size in bytes. Return -1 on failure."""
handle: w32.win32base.HANDLE = w32.win32file.CreateFileA(
path, w32.win32file.GENERIC_READ, w32.win32file.FILE_SHARE_READ,
t.CPtr(0), w32.win32file.OPEN_EXISTING, w32.win32file.FILE_ATTRIBUTE_NORMAL, t.CPtr(0)
@@ -70,18 +70,18 @@ def getsize(path: str) -> int64:
# =============================================================================
# 工作目录
# Working Directory
# =============================================================================
def getcwd() -> str:
"""返回当前工作目录(通过 _mbuddy 分配内存)"""
"""Return the current working directory (memory allocated via _mbuddy)"""
buf: bytes = _mbuddy.alloc(MAX_PATH_LEN + 1)
if buf == None: return None
length: ULONG = w32.win32file.GetCurrentDirectoryA(MAX_PATH_LEN + 1, buf)
if length == 0:
return None
if length > MAX_PATH_LEN:
# 缓冲区不够,重新分配
# Buffer too small; reallocate
buf2: bytes = _mbuddy.alloc(length + 1)
if buf2 == None: return None
w32.win32file.GetCurrentDirectoryA(length + 1, buf2)
@@ -92,7 +92,7 @@ def getcwd() -> str:
def chdir(path: str) -> int:
"""切换当前工作目录。成功返回 0,失败返回 -1。"""
"""Switch the current working directory. Return 0 on success, -1 on failure."""
result: BOOL = w32.win32file.SetCurrentDirectoryA(path)
if result == 0:
return -1
@@ -100,12 +100,12 @@ def chdir(path: str) -> int:
# =============================================================================
# 文件/目录操作
# File/Directory Operations
# =============================================================================
def mkdir(path: str) -> int:
"""创建目录。成功返回 0,失败返回 -1。
路径按 UTF-8 解释时转 UTF-16 走 CreateDirectoryW, 否则退回 A 系列"""
"""Create a directory. Return 0 on success, -1 on failure.
If path is valid UTF-8, convert to UTF-16 and use CreateDirectoryW; otherwise fall back to the A-series."""
if w32.win32base.utf8_is_valid(path) == 0:
result: BOOL = w32.win32file.CreateDirectoryA(path, t.CPtr(0))
if result == 0:
@@ -125,7 +125,7 @@ def mkdir(path: str) -> int:
def remove(path: str) -> int:
"""删除文件。成功返回 0,失败返回 -1。"""
"""Delete a file. Return 0 on success, -1 on failure."""
result: BOOL = w32.win32file.DeleteFileA(path)
if result == 0:
return -1
@@ -133,7 +133,7 @@ def remove(path: str) -> int:
def rmdir(path: str) -> int:
"""删除空目录。成功返回 0,失败返回 -1。"""
"""Delete an empty directory. Return 0 on success, -1 on failure."""
result: BOOL = w32.win32file.RemoveDirectoryA(path)
if result == 0:
return -1
@@ -141,7 +141,7 @@ def rmdir(path: str) -> int:
def rename(old_path: str, new_path: str) -> int:
"""重命名/移动文件或目录。成功返回 0,失败返回 -1。"""
"""Rename/move a file or directory. Return 0 on success, -1 on failure."""
result: BOOL = w32.win32file.MoveFileExA(
old_path, new_path,
w32.win32file.MOVEFILE_REPLACE_EXISTING | w32.win32file.MOVEFILE_COPY_ALLOWED
@@ -152,16 +152,15 @@ def rename(old_path: str, new_path: str) -> int:
def chmod(path: str, mode: int) -> int:
"""修改文件属性(Windows 简化版:只处理只读位)。
mode 的 S_IWUSR 位(0o200)为 0 时设为只读,为 1 时设为可写。
成功返回 0,失败返回 -1。"""
"""Modify file attributes (simplified Windows: read-only bit only).
S_IWUSR (0o200) in mode: 0 sets read-only, 1 sets writable. Return 0 on success, -1 on failure."""
if (mode & 0o200) != 0:
# 可写:清除只读属性
# Writable: clear the read-only attribute
result: BOOL = w32.win32file.SetFileAttributesA(
path, w32.win32file.FILE_ATTRIBUTE_NORMAL
)
else:
# 只读:设置只读属性
# Read-only: set the read-only attribute
result: BOOL = w32.win32file.SetFileAttributesA(
path, w32.win32file.FILE_ATTRIBUTE_READONLY
)
@@ -171,16 +170,13 @@ def chmod(path: str, mode: int) -> int:
# =============================================================================
# 目录列举(FindFirstFile / FindNextFile)
# Directory Listing (FindFirstFile / FindNextFile)
# =============================================================================
def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
"""列举目录下的文件/子目录名。
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
文件名通过 _mbuddy 分配内存。
max_count 为 out_names 数组最大容量。
返回实际列举的条目数。失败返回 0。"""
# 构造搜索模式:path\*
"""List file/subdirectory names under a directory.
out_names is a caller-provided char** array (each entry points to a filename allocated via _mbuddy); max_count is its capacity. Return the count, or 0 on failure."""
# Build search pattern: path\*
path_len: sizet = len(path)
pattern: bytes = _mbuddy.alloc(path_len + 3)
if pattern == None: return 0
@@ -188,7 +184,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
for ch in path:
pattern[i] = ch
i += 1
# 添加 \* 后缀
# Append \* suffix
if path[path_len - 1] != '\\' and path[path_len - 1] != '/':
pattern[i] = '\\'
i += 1
@@ -203,7 +199,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
count: ULONG = 0
while count < max_count:
# 跳过 . 和 ..
# Skip . and ..
name: str = find_data.cFileName
if name[0] == '.':
if name[1] == 0:
@@ -217,7 +213,7 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
break
continue
# 复制文件名到新分配的内存
# Copy the filename into newly allocated memory
name_len: sizet = len(name)
name_buf: bytes = _mbuddy.alloc(name_len + 1)
if name_buf == None: break
@@ -237,11 +233,10 @@ def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
# =============================================================================
# 命令执行
# Command Execution
# =============================================================================
def system(cmd: str) -> int:
"""执行命令行命令(通过 C 运行时 system())。
命令输出直接到子进程的终端(继承父进程 stdout/stderr)。
返回命令的退出码(0 表示成功)。"""
"""Execute a command line command (via the C runtime system()).
Output goes to the child terminal (inherits parent stdout/stderr). Return the exit code (0 = success)."""
return stdlib.system(cmd)
+50 -51
View File
@@ -5,32 +5,32 @@ import memhub
import platmacro
# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化)
# Module-level global MBuddy pointer (initialized by the user in main)
_mbuddy: memhub.MemBuddy*
# =============================================================================
# 路径分隔符
# Path Separators
# =============================================================================
def sep() -> char:
"""返回当前平台的路径分隔符(Windows: \\,POSIX: /)"""
"""Return the platform path separator (Windows: \\, POSIX: /)"""
if platmacro.IS_WINDOWS:
return '\\'
return '/'
def _is_sep(ch: char) -> bool:
"""判断字符是否为路径分隔符(/ 或 \\,跨平台识别)"""
"""Return True if ch is a path separator (/ or \\, cross-platform)"""
return ch == '/' or ch == '\\'
# =============================================================================
# 内部辅助函数
# Internal Helpers
# =============================================================================
def _dup_str(s: str) -> str:
"""复制字符串(通过全局 MBuddy 分配内存)"""
"""Duplicate a string; memory allocated via the global MBuddy"""
if s == None: return None
length: sizet = len(s)
buf: bytes = _mbuddy.alloc(length + 1)
@@ -44,7 +44,7 @@ def _dup_str(s: str) -> str:
def _substr(s: str, start: sizet, end: sizet) -> str:
"""提取子串 s[start:end](通过全局 MBuddy 分配内存)"""
"""Extract substring s[start:end]; memory allocated via the global MBuddy"""
if s == None: return None
if start >= end: return _dup_str("")
result_len: sizet = end - start
@@ -61,18 +61,17 @@ def _substr(s: str, start: sizet, end: sizet) -> str:
# =============================================================================
# 路径判断
# Path Predicates
# =============================================================================
def isabs(path: str) -> bool:
"""判断路径是否为绝对路径。
POSIX: 以 / 开头。
Windows: 以 \\ 开头(UNC)或 X:\\ / X:/ 开头(盘符)。"""
"""Return True if path is absolute.
POSIX: starts with /. Windows: starts with \\ (UNC) or X:\\ / X:/ (drive)."""
if path == None: return False
if path[0] == 0: return False
if path[0] == '/' or path[0] == '\\': return True
if platmacro.IS_WINDOWS:
# 检查 X:\ 或 X:/ 模式
# Check for X:\ or X:/ pattern
c0: char = path[0]
if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'):
if path[1] == ':':
@@ -82,11 +81,11 @@ def isabs(path: str) -> bool:
# =============================================================================
# 路径拼接与分割
# Path Join and Split
# =============================================================================
def join(path1: str, path2: str) -> str:
"""拼接两个路径。如果 path2 是绝对路径,返回 path2 的副本。"""
"""Join two paths. If path2 is absolute, return a copy of path2."""
if path1 == None or path1[0] == 0:
return _dup_str(path2)
if path2 == None or path2[0] == 0:
@@ -97,7 +96,7 @@ def join(path1: str, path2: str) -> str:
len1: sizet = len(path1)
len2: sizet = len(path2)
# 检查 path1 是否以分隔符结尾
# Check whether path1 ends with a separator
need_sep: bool = True
if _is_sep(path1[len1 - 1]):
need_sep = False
@@ -109,18 +108,18 @@ def join(path1: str, path2: str) -> str:
buf: bytes = _mbuddy.alloc(total)
if buf == None: return None
# 复制 path1
# Copy path1
i: sizet = 0
for ch in path1:
buf[i] = ch
i += 1
# 添加分隔符
# Add separator
if need_sep:
buf[i] = sep()
i += 1
# 复制 path2
# Copy path2
for ch in path2:
buf[i] = ch
i += 1
@@ -130,54 +129,54 @@ def join(path1: str, path2: str) -> str:
def basename(path: str) -> str:
"""返回路径的文件名部分(最后一个分隔符之后的内容)"""
"""Return the filename part of path (content after the last separator)"""
if path == None: return None
length: sizet = len(path)
if length == 0: return _dup_str("")
# 从后向前找分隔符
# Scan backward for a separator
i: sizet = length
while i > 0:
i -= 1
if _is_sep(path[i]):
return _substr(path, i + 1, length)
# 没有分隔符,返回整个路径的副本
# No separator; return a copy of the whole path
return _dup_str(path)
def dirname(path: str) -> str:
"""返回路径的目录部分(最后一个分隔符之前的内容)"""
"""Return the directory part of path (content before the last separator)"""
if path == None: return None
length: sizet = len(path)
if length == 0: return _dup_str("")
# 从后向前找分隔符
# Scan backward for a separator
i: sizet = length
while i > 0:
i -= 1
if _is_sep(path[i]):
if i == 0:
# 路径以分隔符开头,返回根目录
# Path starts with a separator; return the root directory
return _dup_str("/")
return _substr(path, 0, i)
# 没有分隔符,返回空字符串
# No separator; return an empty string
return _dup_str("")
# =============================================================================
# 扩展名处理
# Extension Handling
# =============================================================================
def splitext_name(path: str) -> str:
"""返回路径去除扩展名后的主名部分。
扩展名从最后一个 '.' 开始(该 '.' 不能是路径最后一个组件的第一个字符)。"""
"""Return the stem of path with the extension removed.
The extension starts at the last '.' (not the first char of the last component)."""
if path == None: return None
length: sizet = len(path)
if length == 0: return _dup_str("")
# 从后向前找 '.',遇到分隔符停止
# Scan backward for '.'; stop at a separator
i: sizet = length
while i > 0:
i -= 1
@@ -186,17 +185,17 @@ def splitext_name(path: str) -> str:
if _is_sep(path[i]):
break
# 没有扩展名,返回整个路径的副本
# No extension; return a copy of the whole path
return _dup_str(path)
def splitext_ext(path: str) -> str:
"""返回路径的扩展名部分(包含 '.')。无扩展名时返回空字符串。"""
"""Return the extension part of path (including '.'). Return an empty string if none."""
if path == None: return None
length: sizet = len(path)
if length == 0: return _dup_str("")
# 从后向前找 '.',遇到分隔符停止
# Scan backward for '.'; stop at a separator
i: sizet = length
while i > 0:
i -= 1
@@ -205,22 +204,22 @@ def splitext_ext(path: str) -> str:
if _is_sep(path[i]):
break
# 没有扩展名,返回空字符串
# No extension; return an empty string
return _dup_str("")
# =============================================================================
# 路径规范化
# Path Normalization
# =============================================================================
def normpath(path: str) -> str:
"""规范化路径:统一分隔符、消除多余的 . 和 .. 组件、合并连续分隔符。
注意:不解析符号链接,不调用系统 API。"""
"""Normalize path: unify separators, remove redundant . and .. components, merge consecutive separators.
Note: does not resolve symlinks or call system APIs."""
if path == None: return None
length: sizet = len(path)
if length == 0: return _dup_str(".")
# 分配输出缓冲区(最坏情况:长度不变 + 前缀 /)
# Allocate output buffer (worst case: same length + leading /)
buf: bytes = _mbuddy.alloc(length + 2)
if buf == None: return None
@@ -228,7 +227,7 @@ def normpath(path: str) -> str:
i: sizet = 0
s: char = sep()
# 处理绝对路径前缀
# Handle the absolute path prefix
is_absolute: bool = False
if _is_sep(path[0]):
is_absolute = True
@@ -236,7 +235,7 @@ def normpath(path: str) -> str:
out_pos += 1
i = 1
# Windows 盘符前缀(X:\ 或 X:/)
# Windows drive prefix (X:\ or X:/)
drive_prefix_len: sizet = 0
if platmacro.IS_WINDOWS:
if length >= 3:
@@ -254,48 +253,48 @@ def normpath(path: str) -> str:
i = 3
is_absolute = True
# 逐组件处理
# Process component by component
while i < length:
# 跳过连续分隔符
# Skip consecutive separators
while i < length and _is_sep(path[i]):
i += 1
if i >= length:
break
# 提取组件
# Extract component
comp_start: sizet = i
while i < length and not _is_sep(path[i]):
i += 1
comp_len: sizet = i - comp_start
# 处理 "." 组件:跳过
# Handle "." component: skip
if comp_len == 1 and path[comp_start] == '.':
continue
# 处理 ".." 组件:回退一级
# Handle ".." component: go up one level
if comp_len == 2 and path[comp_start] == '.' and path[comp_start + 1] == '.':
if out_pos > drive_prefix_len:
# 回退到上一个分隔符前
# Rewind to just before the previous separator
out_pos -= 1
while out_pos > drive_prefix_len and not _is_sep(buf[out_pos - 1]):
out_pos -= 1
# 如果回退到了根目录前缀,不删除前缀
# If rewound to the root prefix, keep the prefix
if out_pos == drive_prefix_len and is_absolute:
out_pos = drive_prefix_len
if drive_prefix_len == 0:
# POSIX 根:保留 /
# POSIX root: keep /
buf[out_pos] = s
out_pos += 1
continue
# 普通组件:复制到输出
# Normal component: copy to output
if out_pos > drive_prefix_len and not (drive_prefix_len == 0 and out_pos == 1 and is_absolute):
buf[out_pos] = s
out_pos += 1
elif out_pos == 0 and not is_absolute:
pass # 第一个组件,不需要前导分隔符
pass # First component, no leading separator needed
elif is_absolute and out_pos == drive_prefix_len:
# 已经有前导分隔符,不重复添加
# Leading separator already present, do not add again
pass
else:
buf[out_pos] = s
@@ -307,7 +306,7 @@ def normpath(path: str) -> str:
out_pos += 1
j += 1
# 处理空结果
# Handle empty result
if out_pos == 0:
buf[out_pos] = '.'
out_pos += 1
+42 -71
View File
@@ -34,12 +34,12 @@ define HTTP_PORT = 80
define HTTP_BUFSIZ = 4096
define GZIP_BUFSIZ = 65536
# 模块内重定向 / 失败诊断(静态缓冲)
# Module-internal redirect / failure diagnostics (static buffer)
_http_err_store: t.CArray[char, 512]
def last_error() -> str:
"""返回 HTTP 层最近一次失败的原因(无则空串)。"""
"""Last HTTP-layer failure reason (empty string if none)."""
return _http_err_store
@@ -61,8 +61,8 @@ class Response:
status_code: INT # HTTP status code (e.g., 200, 404)
text: str # Response body text
body_len: INT # Response body length
_raw_buf: str # 原始响应缓冲(含头与正文,release 释放)
_body_owned: str # 独立解码正文缓冲(chunked 等解码结果,release 一并释放)
_raw_buf: str # raw response buffer (headers+body, freed by release)
_body_owned: str # separately decoded body buffer (chunked etc., freed by release)
# ============================================================
@@ -185,7 +185,7 @@ def _FindBodyStart(resp_text: str, resp_len: INT) -> INT:
# ============================================================
# 公共辅助(供 requests.https 等子模块复用)
# Public helpers (reused by requests.https and other submodules)
# ============================================================
def create_response() -> Response*:
@@ -207,35 +207,29 @@ def find_body_start(resp_text: str, resp_len: INT) -> INT:
# ============================================================
# 响应解析(header 查询 / chunked 解码 / 统一 parse_response)
# Response parsing (header lookup / chunked decode / unified parse_response)
# ============================================================
def _CIEq(a: char, b: char) -> bool:
"""大小写不敏感字符相等(仅 ASCII)。"""
"""Case-insensitive char equality (ASCII only)."""
if a >= 'A' and a <= 'Z': a = char(INT(a) - INT('A') + INT('a'))
if b >= 'A' and b <= 'Z': b = char(INT(b) - INT('A') + INT('a'))
return a == b
def header_value(resp: Response*, name: str, out: str, out_size: INT) -> INT:
"""在原始响应中按名查询响应头(大小写不敏感,返回首值)。
Args:
resp: Response(其 _raw_buf 保留原始响应)
name: 头名称,如 "Content-Type"
out / out_size: 值写入缓冲
Returns:
0 找到并拷贝;-1 未找到或参数非法。
"""Look up a response header by name in the raw response (case-insensitive, first value).
Returns 0 on found+copied; -1 if missing or bad args.
"""
if resp is None or resp._raw_buf is None or name is None or out is None: return -1
p: str = resp._raw_buf
# 跳过状态行
# Skip status line
while p[0] != '\0' and p[0] != '\n':
p += 1
if p[0] == '\n': p += 1
# 逐头行匹配
# Match each header line
while p[0] != '\0' and p[0] != '\r' and p[0] != '\n':
h: str = p
q: str = name
@@ -246,7 +240,7 @@ def header_value(resp: Response*, name: str, out: str, out_size: INT) -> INT:
break
h += 1
q += 1
# name 匹配后允许 [空格]* ':'(RFC 无空格,宽松兼容)
# Allow [spaces]* ':' after name (RFC has none; lenient)
while h[0] == ' ':
h += 1
if matched and h[0] == ':':
@@ -260,7 +254,7 @@ def header_value(resp: Response*, name: str, out: str, out_size: INT) -> INT:
h += 1
out[i] = 0
return 0
# 跳到下一行
# Skip to next line
while p[0] != '\0' and p[0] != '\n':
p += 1
if p[0] == '\n': p += 1
@@ -276,20 +270,15 @@ def _HexVal(ch: char) -> INT:
def decode_chunked(src: str, src_len: INT, dst: str, dst_cap: INT) -> INT:
"""按 RFC 7230 解码 chunked 正文(不处理 chunk-ext,trailer 忽略)。
Args:
src / src_len: chunked 编码数据
dst / dst_cap: 解码输出缓冲
Returns:
解码字节数;失败返回 -1。
"""Decode chunked body per RFC 7230 (chunk-ext ignored, trailer ignored).
Returns decoded byte count; -1 on failure.
"""
if src is None or dst is None: return -1
r: INT = 0
w: INT = 0
while r < src_len:
# 1) chunk size(hex,可带 ;ext 扩展)
# 1) chunk size (hex, optional ;ext)
size: INT = 0
digit_seen: bool = False
while r < src_len:
@@ -302,13 +291,13 @@ def decode_chunked(src: str, src_len: INT, dst: str, dst_cap: INT) -> INT:
size = size * 16 + hv
r += 1
if not digit_seen: return -1
# 2) 跳过扩展与行尾
# 2) Skip extension and line end
while r < src_len and src[r] != '\n':
r += 1
if r < src_len and src[r] == '\n': r += 1
# 3) 结束块
# 3) Terminator chunk
if size == 0: return w
# 4) 拷贝数据 + 跳过 CRLF
# 4) Copy data + skip CRLF
if w + size > dst_cap: return -1
for k in range(size):
dst[w + k] = src[r + k]
@@ -321,13 +310,8 @@ def decode_chunked(src: str, src_len: INT, dst: str, dst_cap: INT) -> INT:
def parse_response(resp: Response*, total: INT) -> INT:
"""统一解析已接收的原始响应并填充 Response 字段。
- 解析状态码 / 定位正文
- Transfer-Encoding: chunked 时解码到独立缓冲(_body_owned),
原始缓冲 _raw_buf 保留(供 header_value 查询)
Returns:
0 成功;-1 失败(此时 text=None / body_len=0)。
"""Parse the received raw response into Response fields; chunked Transfer-Encoding decodes
into _body_owned while _raw_buf is kept. Returns 0 ok; -1 fail (text=None, body_len=0).
"""
if resp is None or resp._raw_buf is None: return -1
buf: str = resp._raw_buf
@@ -340,7 +324,7 @@ def parse_response(resp: Response*, total: INT) -> INT:
resp.body_len = 0
return 0
# 检测 Transfer-Encoding: chunked
# Detect Transfer-Encoding: chunked
te_buf: t.CArray[char, 64]
chunked: bool = False
if header_value(resp, "Transfer-Encoding", te_buf, 64) == 0:
@@ -367,7 +351,7 @@ def parse_response(resp: Response*, total: INT) -> INT:
resp.text = buf + body_offset
resp.body_len = total - body_offset
# ---- Content-Encoding: gzip 解压(zlib.pyzlib,纯 Viper 实现)----
# ---- Content-Encoding: gzip decompress (zlib.pyzlib, pure Viper) ----
ce_buf: t.CArray[char, 64]
if header_value(resp, "Content-Encoding", ce_buf, 64) == 0 \
and string.strstr(ce_buf, "gzip") is not None \
@@ -385,18 +369,18 @@ def parse_response(resp: Response*, total: INT) -> INT:
resp.text = gz_str
resp.body_len = INT(olen2)
else:
# 解压失败:保留原始 body,记录诊断信息
# Decompress failed: keep raw body, record diagnostics
stdio.snprintf(_http_err_store, 512, "gzip decode failed: %s", pyzlib.get_error())
return 0
# ============================================================
# 公共 API
# Public API
# ============================================================
def _HttpGetOnce(url: str) -> Response*:
"""HTTP GET 单次请求(不跟随重定向)。请求头带 Accept-Encoding: gzip。"""
"""HTTP GET single request (no redirect). Sends Accept-Encoding: gzip."""
if _mbuddy is None: return None
resp: Response* = _AllocResponse()
@@ -469,7 +453,7 @@ def _HttpGetOnce(url: str) -> Response*:
def _HttpPostOnce(url: str, data: str = None, content_type: str = "application/x-www-form-urlencoded") -> Response*:
"""HTTP POST 单次请求(不跟随重定向)。请求头带 Accept-Encoding: gzip。"""
"""HTTP POST single request (no redirect). Sends Accept-Encoding: gzip."""
if _mbuddy is None: return None
resp: Response* = _AllocResponse()
@@ -545,13 +529,12 @@ def _HttpPostOnce(url: str, data: str = None, content_type: str = "application/x
# ============================================================
# 重定向跟随
# Redirect following
# ============================================================
def _ResolveRedirectUrl(base: str, loc: str, out: str, cap: INT) -> INT:
"""把 Location 合并为完整 URL(支持绝对 / 协议相对 / 根相对),结果写 out。
Returns: 0 成功;-1 失败。
"""Merge Location into a full URL (absolute / protocol-relative / root-relative), written to out.
Returns 0 on success; -1 on failure.
"""
if base is None or loc is None or out is None: return -1
lb: INT = INT(string.strlen(loc))
@@ -559,7 +542,7 @@ def _ResolveRedirectUrl(base: str, loc: str, out: str, cap: INT) -> INT:
if lb >= cap: return -1
string.strcpy(out, loc)
return 0
# 取 base 的 scheme://authority 前缀
# Take scheme://authority prefix of base
q: str = base
while q[0] != '\0' and q[0] != ':': q += 1
if q[0] != ':': return -1
@@ -591,7 +574,7 @@ def _ResolveRedirectUrl(base: str, loc: str, out: str, cap: INT) -> INT:
def _HttpRedirectLoop(method: str, url: str, data: str, content_type: str) -> Response*:
"""GET/POST + 重定向跟随(http:// 链,最多 5 跳)。"""
"""GET/POST with redirect following (http:// chain, max 5 hops)."""
if _mbuddy is None: return None
cur: str = _mbuddy.alloc(2048)
@@ -660,18 +643,14 @@ def _IsHttpRedirect(code: INT) -> bool:
def get(url: str) -> Response*:
"""HTTP GET(自动跟随 3xx 重定向,最多 5 跳)。
Args:
url: Full URL (e.g., "http://example.com/path")
Returns:
Pointer to a Response object, None on failure (see last_error()).
"""HTTP GET (follows 3xx redirects, max 5 hops). Returns Response* or None
on failure (see last_error()).
"""
return _HttpRedirectLoop("GET", url, None, None)
def post(url: str, data: str = None, content_type: str = "application/x-www-form-urlencoded") -> Response*:
"""HTTP POST(自动跟随 3xx 重定向,最多 5 跳)。"""
"""HTTP POST (follows 3xx redirects, max 5 hops)."""
return _HttpRedirectLoop("POST", url, data, content_type)
@@ -691,20 +670,12 @@ def release(resp: Response*):
# ============================================================
# 大文件 Range 下载原语(requests.download 使用)
#
# _DlOnce 单次发起 "Range: bytes=start-end" 的 GET(不带 Accept-Encoding,
# 保证响应是原始字节),把响应体写入调用方提供的 dst 缓冲。
# 线程安全:本函数不使用 _mbuddy(响应/请求缓冲一律 stdlib),
# socket 连接独立,供多线程并行切片下载并发调用。
#
# 返回: >=0 响应体字节数;-1 参数错;-2 3xx 重定向(Location 写 out_loc,
# 由 download 模块跟随);-3 其它失败(last_error() 查看原因)。
# out_status/out_total 分别输出 HTTP 状态码与 Content-Range 总大小。
# Large-file Range download primitive for requests.download: _DlOnce issues a "Range: bytes=
# start-end" GET (no Accept-Encoding) into dst. Thread-safe (no _mbuddy, own socket). >=0 bytes; -1/-2/-3.
# ============================================================
def _RawHeaderValue(buf: str, total: INT, name: str, out: str, out_size: INT) -> INT:
"""对原始响应头区做大小写不敏感查询(0=命中并拷贝值;-1=未命中)。"""
"""Case-insensitive lookup on raw response header area (0=hit+copied; -1=miss)."""
if buf is None or name is None or out is None or out_size <= 0:
return -1
body: INT = _FindBodyStart(buf, total)
@@ -741,7 +712,7 @@ def _RawHeaderValue(buf: str, total: INT, name: str, out: str, out_size: INT) ->
def _StatusRaw(buf: str) -> INT:
"""从 "HTTP/1.1 206 ..." 首行解析状态码。"""
"""Parse status code from the first line ("HTTP/1.1 206 ...")."""
if buf is None:
return 0
i: INT = 0
@@ -759,7 +730,7 @@ def _StatusRaw(buf: str) -> INT:
def _CLenOf(buf: str, total: INT) -> INT:
"""读取 Content-Length(无则 -1)。"""
"""Read Content-Length (-1 if absent)."""
hbuf: t.CArray[char, 32]
if _RawHeaderValue(buf, total, "Content-Length", hbuf, 32) != 0:
return -1
@@ -772,7 +743,7 @@ def _CLenOf(buf: str, total: INT) -> INT:
def _CRTotalOf(buf: str, total: INT) -> sizet:
"""从 Content-Range: bytes s-e/TOTAL 解析 TOTAL(非 206/缺头则 0)。"""
"""Parse TOTAL from Content-Range: bytes s-e/TOTAL (0 if not 206 / header absent)."""
hbuf: t.CArray[char, 128]
if _RawHeaderValue(buf, total, "Content-Range", hbuf, 128) != 0:
return 0
@@ -792,7 +763,7 @@ def _CRTotalOf(buf: str, total: INT) -> sizet:
def _DlOnce(url: str, start: sizet, count: INT, dst: str, dst_cap: INT,
out_status: INT*, out_loc: str, out_loccap: INT,
out_total: sizet*) -> INT:
"""HTTP 单次 Range 拉取(线程安全,见模块注释)。"""
"""HTTP single Range fetch (thread-safe; see module comment)."""
if url is None or dst is None or dst_cap <= 0 \
or out_status is None or out_total is None:
return -1
+32 -47
View File
@@ -40,7 +40,7 @@ _dl_err_store: t.CArray[char, 512]
def last_error() -> str:
"""返回最近一次下载失败原因文本(无则空串)。"""
"""Last download failure reason (empty string if none)."""
return _dl_err_store
@@ -52,7 +52,7 @@ def _dl_set_err(text: str):
def _dl_strdup(s: str) -> str:
"""stdlib 堆上复制字符串(调用方 free),None 失败。"""
"""Duplicate string on stdlib heap (caller frees); None on failure."""
if s is None:
return None
n: sizet = string.strlen(s)
@@ -66,13 +66,14 @@ def _dl_strdup(s: str) -> str:
# ============================================================
# 单切片拉取(含重定向跟随,≤5 跳;http/https 按 scheme 分发)
# Single-slice fetch (redirect follow, <=5 hops; dispatch by http/https scheme)
# ============================================================
def _dl_slice(url: str, pos: sizet, want: INT, verify: bool, ca_file: str,
dst: str, dst_cap: INT, out_total: sizet*) -> INT:
"""拉取 [pos, pos+want) 一片,返回体字节数写入 dst(dst 内正文起始需
调用方用 _dl_body_off 定位);-2 重定向超限;-3 网络/服务器失败。"""
"""Fetch slice [pos, pos+want); body bytes written to dst (locate body via _dl_body_off).
Returns -2 on too many redirects; -3 on network/server failure.
"""
if url is None or dst is None or out_total is None:
return -1
cur: str = _dl_strdup(url)
@@ -118,16 +119,16 @@ def _dl_slice(url: str, pos: sizet, want: INT, verify: bool, ca_file: str,
def _dl_body_off(dst: str, dst_cap: INT) -> INT:
"""定位响应体(头结束处)在 dst 中的偏移,找不到返回 -1。"""
"""Offset of response body (after headers) in dst; -1 if not found."""
return requests._FindBodyStart(dst, dst_cap)
# ============================================================
# 文件写入(按偏移落盘,支持多线程并发写不同区段)
# File write (write at offset; supports concurrent threads writing distinct ranges)
# ============================================================
def _dl_write_at(path: str, src: str, length: INT, offset: sizet) -> INT:
"""打开文件(不存在则创建),Seek 到 offset 写 length 字节。0 成功;-1 失败。"""
"""Open file (create if absent), seek to offset, write length bytes. 0 ok; -1 fail."""
if path is None or src is None or length <= 0:
return -1
h: wb.HANDLE = wf.CreateFileA(
@@ -136,21 +137,14 @@ def _dl_write_at(path: str, src: str, length: INT, offset: sizet) -> INT:
None, wf.OPEN_ALWAYS, wf.FILE_ATTRIBUTE_NORMAL, None)
if h == wb.INVALID_HANDLE_VALUE:
return -1
# 用 32 位版 SetFilePointer:lDistanceToMove 按值传 LONG,无结构体 ABI 歧义。
# 不用 SetFilePointerEx:win32file.vp:93 声明为
# liDistanceToMove: LARGE_INTEGER | t.CPtr
# 传 c.Addr(li) 时该联合实参按"指针"传,而真实 Win32 的 liDistanceToMove 是
# 按值传 8 字节 ⇒ 移动距离变成 li 的地址数值(与 VET 已修掉的 VFs.SeekTo 同根因,
# 见 VET/App/lib/VPe.vp:286 的绕行说明);可用先例:includes/w32/fileio.vp:169
# 的 File.seek 就走 32 位版。
# lpDistanceToMoveHigh=NULL ⇒ 距离限定 32 位有符号(|offset| < 2 GiB),
# 故先显式上界守卫:超范围直接判失败,绝不静默 seek 到错误位置。
# Use 32-bit SetFilePointer (lDistanceToMove passed by value as LONG; no struct ABI ambiguity).
# lpDistanceToMoveHigh=NULL limits distance to 32-bit signed (<2GiB), so guard the upper bound first.
if offset > sizet(0x7FFFFFFF):
wb.CloseHandle(h)
return -1
ok_seek: LONG = wf.SetFilePointer(h, LONG(offset), None, wf.FILE_BEGIN)
# INVALID_SET_FILE_POINTER(0xFFFFFFFF) 截断成 LONG 即 -1。因上面已把 offset
# 限制在 0..0x7FFFFFFF,0xFFFFFFFF 不可能是"合法偏移",无需再叠 GetLastError 消歧。
# INVALID_SET_FILE_POINTER(0xFFFFFFFF) truncates to LONG -1; offset is already clamped to
# 0..0x7FFFFFFF, so 0xFFFFFFFF cannot be a valid offset and no GetLastError check is needed.
if ok_seek == -1:
wb.CloseHandle(h)
return -1
@@ -165,7 +159,7 @@ def _dl_write_at(path: str, src: str, length: INT, offset: sizet) -> INT:
def _dl_truncate(path: str) -> INT:
"""CREATE_ALWAYS 建(截断)空文件。0 成功;-1 失败。"""
"""Create (truncate) an empty file via CREATE_ALWAYS. 0 ok; -1 fail."""
h: wb.HANDLE = wf.CreateFileA(
path, wf.GENERIC_WRITE,
wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE | wf.FILE_SHARE_DELETE,
@@ -177,14 +171,14 @@ def _dl_truncate(path: str) -> INT:
# ============================================================
# 探测:总大小 / Range 支持(首片 bytes=0-0)
# Probe: total size / Range support (first slice bytes=0-0)
# ============================================================
def _dl_probe(url: str, verify: bool, ca_file: str,
out_total: sizet*) -> INT:
"""探测服务器总大小与 Range 支持。
返回 0=支持 Range 且 out_total 有效;1=服务器返回 200(不支持 Range,
退化为整段下载,out_total=Content-Length);-3 失败。"""
"""Probe server total size and Range support. Returns 0=Range supported (out_total valid);
1=server returned 200 (no Range, full download, out_total=Content-Length); -3 fail.
"""
*out_total = 0
buf: str = stdlib.malloc(8192)
if buf is None:
@@ -204,14 +198,14 @@ def _dl_probe(url: str, verify: bool, ca_file: str,
# ============================================================
# 下载一个区间带([b_start, b_end)),逐片拉取并落盘
# Download one band ([b_start, b_end)), fetching and writing slice by slice
# ============================================================
def _dl_download_band(path: str, url: str, is_https: INT, verify: bool, ca_file: str,
b_start: sizet, b_end: sizet, chunk: INT,
buf: str, buf_cap: INT,
p_done: sizet*) -> INT:
"""0 成功;-1 网络/服务器;-2 写入失败。p_done 累计已写字节。"""
"""0 ok; -1 network/server; -2 write failure. p_done accumulates bytes written."""
pos: sizet = b_start
while pos < b_end:
want: INT = chunk
@@ -238,7 +232,7 @@ def _dl_download_band(path: str, url: str, is_https: INT, verify: bool, ca_file:
*p_done = *p_done + sizet(got)
pos += sizet(got)
if got < want:
# 服务端提前结束(正常收尾时仅末片 < want)
# Server ended early (only the last slice is < want on normal completion)
if pos >= b_end:
break
_dl_set_err("short slice")
@@ -247,7 +241,7 @@ def _dl_download_band(path: str, url: str, is_https: INT, verify: bool, ca_file:
# ============================================================
# 多线程 worker
# Multi-thread worker
# ============================================================
class DlJob:
path: str
@@ -280,23 +274,14 @@ def _dl_worker(arg: VOIDPTR) -> t.CPtr:
# ============================================================
# 公共 API
# Public API
# ============================================================
def download(url: str, path: str, threads: INT = 1,
chunk_size: INT = DL_DEFAULT_CHUNK,
verify: bool = True, ca_file: str = None) -> INT:
"""大文件切片下载。
Args:
url: 支持 http:// 与 https://(Range 分段拉取)
path: 保存路径(不存在自动创建;已存在则截断重建)
threads: 1=串行切片;2~8=多线程并行分带下载
chunk_size: 单片大小(字节,默认 256KB)
verify / ca_file: https 证书校验参数(对齐 requests.https)
Returns:
0 成功;-1 参数错误;-2 文件创建失败;-3 服务器不支持/下载失败;
-4 写入失败。last_error() 可查原因文本。
"""Chunked large-file download over http:// or https:// (Range). threads 1=serial,
2~8=parallel bands; chunk_size bytes (default 256KB). Returns 0/-1/-2/-3/-4 (see last_error()).
"""
_dl_err_store[0] = 0
if url is None or path is None or url[0] == 0 or path[0] == 0:
@@ -313,7 +298,7 @@ def download(url: str, path: str, threads: INT = 1,
_dl_set_err("unsupported scheme")
return -1
# 探测总大小与 Range 支持
# Probe total size and Range support
total: sizet = 0
prc: INT = _dl_probe(url, verify, ca_file, &total)
if prc < 0:
@@ -322,7 +307,7 @@ def download(url: str, path: str, threads: INT = 1,
_dl_set_err("cannot determine size")
return -3
# 建立(截断)目标文件
# Create (truncate) target file
if _dl_truncate(path) != 0:
_dl_set_err("create file failed")
return -2
@@ -333,7 +318,7 @@ def download(url: str, path: str, threads: INT = 1,
slab: INT = chunk_size + 8192
if threads == 1:
# ---- 串行切片 ----
# ---- Serial slices ----
buf: str = stdlib.malloc(sizet(slab))
if buf is None:
_dl_set_err("out of memory")
@@ -347,7 +332,7 @@ def download(url: str, path: str, threads: INT = 1,
stdio.printf("[download] done: %zu/%zu bytes\n", done, total)
return 0
# ---- 多线程并行分带 ----
# ---- Multi-thread parallel bands ----
band: sizet = (total + sizet(threads) - 1) / sizet(threads)
ctxs: t.CArray[VOIDPTR, DL_MAX_THREADS]
handles: t.CArray[VOIDPTR, DL_MAX_THREADS]
@@ -406,12 +391,12 @@ def download(url: str, path: str, threads: INT = 1,
_dl_set_err("thread create failed")
return -3
# 等待全部线程结束
# Wait for all threads to finish
for j in range(threads_ok):
wsync.WaitForSingleObject(handles[j], 0xFFFFFFFF)
wb.CloseHandle(handles[j])
# 汇总结果
# Aggregate results
total_done: sizet = 0
failed_job: INT = 0
for k in range(threads_ok):
@@ -420,7 +405,7 @@ def download(url: str, path: str, threads: INT = 1,
if jobk.status != 0:
failed_job = 1
# 释放线程上下文
# Free thread contexts
for m in range(threads_ok):
jobm: DlJob* = ctxs[m]
stdlib.free(jobm.path)
+73 -113
View File
@@ -31,21 +31,18 @@ import memhub
define HTTPS_PORT = 443
# 模块内部状态
# Module-internal state
_ossl_ready: bool = False
_socket_ready: bool = False
_err_store: t.CArray[char, 512] # 最近一次失败原因(静态缓冲)
_info_store: t.CArray[char, 512] # 最近一次成功握手的 TLS 元信息
_scratch_store: t.CArray[char, 512] # 临时拼接缓冲
_last_verify_val: INT = -1 # 最近一次握手后 SSL_get_verify_result(0=通过;-1=未启用)
_last_verify_flag: INT = -1 # 最近一次请求 verify 标志(1=开启;0=关闭;-1=未知)
_err_store: t.CArray[char, 512] # last failure reason (static buffer)
_info_store: t.CArray[char, 512] # TLS metadata of last successful handshake
_scratch_store: t.CArray[char, 512] # scratch concat buffer
_last_verify_val: INT = -1 # last SSL_get_verify_result (0=ok; -1=disabled)
_last_verify_flag: INT = -1 # last request verify flag (1=on; 0=off; -1=unknown)
# ============================================================
# SSE 流式内容接收区(post_stream 专用;单路请求,UI 线程轮询拉取)
# _st_len: 工作线程已写入的内容字节数
# _st_consumed: UI 已消费游标(仅拉取方推进)
# _st_state: 0=运行中 1=完成(2xx) 2=失败
# _st_status: HTTP 状态码(失败网络路径为负错误码)
# SSE streaming receive area (post_stream only; single request, polled by UI thread).
# _st_len written bytes; _st_consumed consumed cursor; _st_state 0=running 1=done(2xx) 2=fail; _st_status HTTP code.
# ============================================================
define STREAM_CAP = 262144
_st_buf: t.CArray[char, 262144]
@@ -57,7 +54,7 @@ _st_err: t.CArray[char, 512]
def stream_begin():
"""开始新一轮流式接收:重置缓冲/游标/状态。"""
"""Start a new streaming receive: reset buffer/cursor/state."""
global _st_len, _st_consumed, _st_state, _st_status
_st_len = 0
_st_consumed = 0
@@ -67,7 +64,7 @@ def stream_begin():
def stream_pull(out: str, cap: INT) -> INT:
"""拉取未消费的增量内容到 out(自动补 0),返回拷贝字节数。"""
"""Copy unconsumed delta into out (NUL-terminated); return bytes copied."""
global _st_consumed
if out is None or cap <= 1:
return 0
@@ -84,60 +81,60 @@ def stream_pull(out: str, cap: INT) -> INT:
def stream_state() -> INT:
"""0=运行中 1=完成(2xx) 2=失败。"""
"""0=running 1=done(2xx) 2=failed."""
return _st_state
def stream_status() -> INT:
"""HTTP 状态码;负数为网络/解析错误。"""
"""HTTP status code; negative means network/parse error."""
return _st_status
def stream_error_text() -> str:
"""失败原因文本(_st_state==2 时)。"""
"""Failure reason text (when _st_state==2)."""
return _st_err
def last_verify_result() -> int:
"""返回最近一次握手的证书校验结果:0 通过;>0 失败码;-1 未开启校验。"""
"""Last cert verify result: 0=ok; >0=failure code; -1=verify disabled."""
return _last_verify_val
def last_verify_enabled() -> int:
"""返回最近一次请求是否开启校验(1=是;0=否;-1=未知)。"""
"""Whether last request enabled verify (1=yes; 0=no; -1=unknown)."""
return _last_verify_flag
# ============================================================
# 内部辅助
# Internal helpers
# ============================================================
def last_error() -> str:
"""返回最近一次失败的原因文本(无则空串)。"""
"""Last failure reason text (empty string if none)."""
return _err_store
def last_tls_info() -> str:
"""返回最近一次成功握手的 TLS 信息(版本/套件/对端主题),无则空串。"""
"""Last successful handshake TLS info (version/cipher/peer subject), empty if none."""
return _info_store
def _RecordErrorText(text: str):
"""把失败原因写入静态缓冲 _err_store。"""
"""Write failure reason into static buffer _err_store."""
if text is None: return
_err_store[0] = 0
stdio.snprintf(_err_store, 512, "%s", text)
def _RecordInfoText(text: str):
"""把 TLS 元信息写入静态缓冲 _info_store。"""
"""Write TLS metadata into static buffer _info_store."""
if text is None: return
_info_store[0] = 0
stdio.snprintf(_info_store, 512, "%s", text)
def _EnsureReady() -> INT:
"""初始化 socket 与 openssl(均幂等)。"""
"""Initialize socket and openssl (both idempotent)."""
global _socket_ready, _ossl_ready
if not _socket_ready:
if socket.SocketInit() != 0:
@@ -153,13 +150,13 @@ def _EnsureReady() -> INT:
def _ParseHttpsUrl(url: str, host_buf: str, host_size: INT, path_buf: str, path_size: INT, port: INT*) -> INT:
"""解析 https://host[:port][/path],端口缺省 443。"""
"""Parse https://host[:port][/path]; default port 443."""
if url is None: return -1
if host_buf is None or path_buf is None or port is None: return -1
p: str = url
# Skip "https://"(8 字节)
# Skip "https://" (8 bytes)
if string.strncmp(p, "https://", 8) == 0:
p += 8
@@ -204,12 +201,12 @@ def _ParseHttpsUrl(url: str, host_buf: str, host_size: INT, path_buf: str, path_
def _ScratchBuf() -> str:
"""返回静态拼接缓冲(512B,复用)。"""
"""Return static concat buffer (512B, reused)."""
return _scratch_store
def _TlsErrText():
"""把最近一次 openssl 错误队列文本写入静态 scratch 缓冲。"""
"""Write last openssl error-queue text into static scratch buffer."""
e: uint32 = openssl.ERR_get_error()
if e == 0:
_scratch_store[0] = 0
@@ -219,16 +216,12 @@ def _TlsErrText():
# ============================================================
# 核心请求
# Core request
# ============================================================
def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str, content_type: str, auth: str = None) -> rawptr[requests.Response]:
"""HTTPS GET/POST 核心实现。
verify=False 时跳过证书链/主机名校验(不推荐);verify=True 时
ca_file 提供 PEM CA bundle,否则使用系统默认 CA 路径。
auth(可选):形如 "Bearer <key>" 的 Authorization 头值,
非空时仅在 POST 请求头中插入 Authorization 头(默认 None = 行为不变)。
"""HTTPS GET/POST core. verify=False skips cert chain/hostname checks (not recommended);
verify=True uses ca_file PEM bundle (else system default); auth adds Authorization header on POST only.
"""
global _last_verify_val, _last_verify_flag
if requests._mbuddy is None: return None
@@ -242,13 +235,13 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
requests.release(resp)
return None
# 临时缓冲(错误文本格式化;openssl 详情走模块级 scratch)
# Temp buffer for error text; openssl details go to module scratch.
errmsg: str = requests._mbuddy.alloc(512)
if errmsg is None:
requests.release(resp)
return None
# ---- 解析 URL ----
# ---- Parse URL ----
host_buf: t.CArray[char, 256]
path_buf: t.CArray[char, 1024]
port: INT = 0
@@ -260,7 +253,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
requests.release(resp)
return None
# ---- 建立 TCP 连接 ----
# ---- Establish TCP connection ----
sock = socket.Socket(socket.AF_INET, socket.SOCK_STREAM)
if sock.fd == socket.INVALID_SOCKET:
_RecordErrorText("socket() failed")
@@ -298,7 +291,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
requests.release(resp)
return None
else:
# 系统默认路径(Windows 上通常为空,证书链校验将失败)
# System default paths (usually empty on Windows; cert chain check will fail).
openssl.SSL_CTX_set_default_verify_paths(ctx)
else:
openssl.SSL_CTX_set_verify(ctx, openssl.SslVerifyNone, None)
@@ -313,13 +306,13 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
return None
openssl.SSL_set_fd(ssl, sock.fd)
# SNI(SSL_set_tlsext_host_name 是宏 → 直接 SSL_ctrl)
# SNI (SSL_set_tlsext_host_name is a macro -> call SSL_ctrl directly)
openssl.SSL_ctrl(ssl, openssl.SslCtrlSetTlsextHostname, openssl.TlsextNametypeHostName, host_buf)
# 主机名校验(TLS 内核对证书 SAN/CN 与 host 比对)
# Hostname verification (TLS core compares cert SAN/CN against host)
if verify:
openssl.SSL_set1_host(ssl, host_buf)
# ---- 握手 ----
# ---- Handshake ----
hret: INT = openssl.SSL_connect(ssl)
if hret <= 0:
want: INT = openssl.SSL_get_error(ssl, hret)
@@ -333,13 +326,13 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
requests.release(resp)
return None
# ---- 记录最近一次证书校验结果(供 last_verify_result 查询)----
# ---- Record last cert verify result (for last_verify_result) ----
if verify:
_last_verify_val = INT(openssl.SSL_get_verify_result(ssl))
else:
_last_verify_val = -1
# ---- 证书链校验结果复核 ----
# ---- Re-check cert chain verify result ----
if verify:
vres: int32 = openssl.SSL_get_verify_result(ssl)
if vres != 0:
@@ -356,7 +349,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
requests.release(resp)
return None
# ---- 记录 TLS 元信息(版本 / 套件 / 对端证书主题)----
# ---- Record TLS metadata (version / cipher / peer subject) ----
name_buf: str = requests._mbuddy.alloc(512)
if name_buf is not None:
peer: openssl.X509Ptr = openssl.SSL_get1_peer_certificate(ssl)
@@ -378,7 +371,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
_RecordInfoText(errmsg)
requests._mbuddy.free(name_buf)
# ---- 组请求头并发送 ----
# ---- Build request headers and send ----
req_buf: str = requests._mbuddy.alloc(requests.HTTP_BUFSIZ)
if req_buf is None:
_RecordErrorText("alloc request buffer failed")
@@ -394,7 +387,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
if is_post:
body_len = INT(string.strlen(data)) if data is not None else 0
ct: str = content_type if content_type is not None else "application/x-www-form-urlencoded"
# 分段组头,便于按需插入 Authorization(auth 非空才加,默认路径输出与原来完全一致)
# Build header in stages so Authorization can be inserted on demand (only when auth non-empty).
hdr_off: INT = 0
hdr_cap: INT = requests.HTTP_BUFSIZ
stdio.snprintf(req_buf, hdr_cap, "POST %s HTTP/1.1\r\nHost: %s\r\n", path_buf, host_buf)
@@ -429,7 +422,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
requests._mbuddy.free(req_buf)
# ---- 接收响应(缓冲自动翻倍增长, 支持 MB 级正文, 如 MNIST .gz 下载) ----
# ---- Receive response (buffer auto-doubles, supports MB bodies) ----
resp_cap: INT = requests.HTTP_BUFSIZ
resp_buf: str = requests._mbuddy.alloc(sizet(resp_cap))
if resp_buf is None:
@@ -445,7 +438,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
while True:
room: INT = resp_cap - 1 - total
if room <= 0:
# 缓冲已满仍可能有数据: 翻倍扩容(硬上限 256MB, 防失控流)
# Buffer full but more data may follow: double capacity (hard cap 256MB, runaway-guard).
if resp_cap >= 268435456:
break
resp_cap = resp_cap * 2
@@ -468,7 +461,7 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
resp_buf[total] = 0
# 关闭 TLS 与 TCP
# Close TLS and TCP
openssl.SSL_shutdown(ssl)
openssl.SSL_free(ssl)
openssl.SSL_CTX_free(ctx)
@@ -482,14 +475,12 @@ def _HttpsRequest(method: str, url: str, verify: bool, ca_file: str, data: str,
# ============================================================
# 公共 API
# Public API
# ============================================================
def _HttpsRedirectLoop(method: str, url: str, verify: bool, ca_file: str, data: str, content_type: str, auth: str = None) -> rawptr[requests.Response]:
"""HTTPS GET/POST + 重定向跟随(跨 http/https,最多 5 跳)。
auth(可选)原样透传给 _HttpsRequest;None 时行为与原来完全一致。
注意:跨主机重定向时 Authorization 同样会透传(仅提示,逻辑未改动)。
"""HTTPS GET/POST with redirect following (cross http/https, max 5 hops).
auth is passed through to _HttpsRequest; note Authorization also follows cross-host redirects.
"""
if requests._mbuddy is None: return None
@@ -535,7 +526,7 @@ def _HttpsRedirectLoop(method: str, url: str, verify: bool, ca_file: str, data:
return None
requests.release(resp)
# 跨协议分流:https → 继续本循环;http → 交给 requests(http)链
# Cross-scheme dispatch: https -> continue loop; http -> hand to requests (http) chain
if string.strncmp(nxt, "https://", 8) != 0:
requests._mbuddy.free(loc)
requests._mbuddy.free(cur)
@@ -553,50 +544,27 @@ def _HttpsRedirectLoop(method: str, url: str, verify: bool, ca_file: str, data:
def get(url: str, verify: bool = True, ca_file: str = None) -> rawptr[requests.Response]:
"""HTTPS GET request(自动跟随 3xx 重定向,最多 5 跳)。
Args:
url: Full URL (e.g., "https://example.com/path")
verify: True 时校验证书链 + 主机名(默认)
ca_file: PEM CA bundle 路径;None 时使用系统默认 CA 路径
Returns:
Pointer to a Response object, None on failure (see last_error()).
"""HTTPS GET (follows 3xx redirects, max 5 hops). verify checks cert chain + hostname;
ca_file is a PEM CA bundle path, None uses system default. Returns Response* or None.
"""
return _HttpsRedirectLoop("GET", url, verify, ca_file, None, None)
def post(url: str, data: str = None, content_type: str = "application/x-www-form-urlencoded",
verify: bool = True, ca_file: str = None, auth: str = None) -> rawptr[requests.Response]:
"""HTTPS POST request(自动跟随 3xx 重定向,最多 5 跳)。
Args:
url: Full URL
data: Request body data
content_type: Content-Type header
verify: True 时校验证书链 + 主机名(默认)
ca_file: PEM CA bundle 路径;None 时使用系统默认 CA 路径
auth: 可选 Authorization 头值(形如 "Bearer <key>"),
非空时随请求头发送;None/空串 = 与旧版完全一致。
Returns:
Pointer to a Response object, None on failure (see last_error()).
"""HTTPS POST (follows 3xx redirects, max 5 hops). verify checks cert chain + hostname;
ca_file PEM CA bundle (None = system default); auth optional "Bearer <key>", sent when non-empty.
"""
return _HttpsRedirectLoop("POST", url, verify, ca_file, data, content_type, auth)
# ============================================================
# SSE 流式 Chat Completions(post_stream)
#
# 与 post() 不同:请求头带 Accept: text/event-stream;响应体按行解析
# SSE(data: {json}),把每段 choices[0].delta.content 抽进 _st_buf 供
# UI 线程 stream_pull() 增量拉取显示。全程阻塞读取,应在后台线程调用。
#
# 返回: >=0 HTTP 状态码(2xx 正常完成;3xx/4xx/5xx 状态仍返回给调用方,
# 正文中的非 SSE 内容不会进 _st_buf);-1/-2 网络/解析失败
# (_st_state=2,失败原因见 stream_error_text()/last_error())。
# SSE streaming Chat Completions (post_stream): sends Accept: text/event-stream and extracts
# choices[0].delta.content from data:{json} lines into _st_buf; blocking, returns HTTP status or -1/-2.
# ============================================================
def _SseDeltaContent(root: rawptr[json.JsonValue]) -> str:
"""从流式 data 事件取 choices[0].delta.content;缺失返回 None。"""
"""Extract choices[0].delta.content from a stream data event; None if absent."""
if root is None or not root.is_object():
return None
cv: rawptr[json.JsonValue] = root["choices"]
@@ -615,7 +583,7 @@ def _SseDeltaContent(root: rawptr[json.JsonValue]) -> str:
def _StreamAppendContent(txt: str):
"""把一段增量内容追加到 _st_buf(超容量则截断丢弃并记录)。"""
"""Append delta content to _st_buf (truncate on overflow)."""
global _st_len
if txt is None:
return
@@ -631,7 +599,7 @@ def _StreamAppendContent(txt: str):
def _SetStreamFail(rc: INT):
"""流式失败收尾:从 _err_store 拷原因文本并置状态。"""
"""Finish stream failure: copy reason from _err_store and set state."""
global _st_state, _st_status
_st_state = 2
_st_status = rc
@@ -640,10 +608,8 @@ def _SetStreamFail(rc: INT):
def post_stream(url: str, data: str, content_type: str = "application/json",
verify: bool = True, ca_file: str = None, auth: str = None) -> INT:
"""SSE 流式 POST(阻塞,应在后台线程调用)。
data 应为已含 "stream":true 的 JSON 请求体。
返回 HTTP 状态码;负数为网络/解析失败(_st_state=2)。
"""SSE streaming POST (blocking; call from a background thread). data must be a JSON body
containing "stream":true. Returns HTTP status; negative means network/parse failure.
"""
global _st_state, _st_status
if requests._mbuddy is None:
@@ -652,12 +618,12 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
_st_state = 0
_st_status = 0
# Winsock/openssl 初始化(幂等;流式首次可能在后台线程执行)
# Winsock/openssl init (idempotent; first call may run on a background thread)
if _EnsureReady() != 0:
_SetStreamFail(-1)
return -1
# ---- 连接/TLS(与 _HttpsRequest 一致)----
# ---- Connect/TLS (same as _HttpsRequest) ----
errmsg: str = requests._mbuddy.alloc(512)
if errmsg is None:
return -1
@@ -754,7 +720,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
requests._mbuddy.free(errmsg)
return -1
# ---- 组请求头(POST + Accept: text/event-stream)并发送 ----
# ---- Build request headers (POST + Accept: text/event-stream) and send ----
req_buf: str = requests._mbuddy.alloc(requests.HTTP_BUFSIZ)
if req_buf is None:
_RecordErrorText("alloc request buffer failed")
@@ -794,7 +760,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
openssl.SSL_write(ssl, data, body_len)
requests._mbuddy.free(req_buf)
# ---- 读取响应:跳过响应头 → SSE 行解析 → 抽 delta.content ----
# ---- Read response: skip headers -> parse SSE lines -> extract delta.content ----
rd: str = requests._mbuddy.alloc(16384)
if rd is None:
_RecordErrorText("alloc stream buffer failed")
@@ -805,7 +771,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
requests._mbuddy.free(errmsg)
return -1
# JSON 解析池(事件粒度 reset,足够容纳单个 data 事件)
# JSON pool (reset per event, sized for one data event)
pmem: str = stdlib.malloc(524288)
if pmem is None:
_RecordErrorText("alloc json pool failed")
@@ -829,15 +795,15 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
if n <= 0:
break
had_read = 1
# 1) 追加到 leftover
# 1) Append to leftover
ai: INT = 0
while ai < n and llen < 16383:
leftover[llen] = rd[ai]
llen += 1
ai += 1
# 2) 逐行处理
# 2) Process line by line
while True:
# 找行尾 \n
# Find line end \n
nl: INT = -1
for k in range(llen):
if leftover[k] == 10:
@@ -852,7 +818,7 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
line: str = leftover
if header_done == 0:
if string.strncmp(line, "HTTP/", 5) == 0:
# 状态码:HTTP/1.1 200 OK
# Status code: HTTP/1.1 200 OK
sp: INT = 0
while line[sp] != 0 and line[sp] != ' ':
sp += 1
@@ -878,13 +844,13 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
dtx: str = _SseDeltaContent(ev)
if dtx is not None:
_StreamAppendContent(dtx)
# 3) 移除已处理行
# 3) Remove processed line
rem: INT = llen - (nl + 1)
for j2 in range(rem):
leftover[j2] = leftover[nl + 1 + j2]
llen = rem
# ---- 收尾 ----
# ---- Wrap up ----
openssl.SSL_shutdown(ssl)
openssl.SSL_free(ssl)
openssl.SSL_CTX_free(ctx)
@@ -911,21 +877,15 @@ def post_stream(url: str, data: str, content_type: str = "application/json",
# ============================================================
# 大文件 Range 下载原语(requests.download 使用,与 requests._DlOnce 镜像)
#
# 线程安全:本函数不使用 _mbuddy(请求/响应缓冲一律 stdlib),每个连接独立
# SSL_CTX/SSL/socket,供多线程并行切片并发调用(OPENSSL_init_ssl 由主线程
# 先 _EnsureReady 预热一次)。
#
# 返回: >=0 响应体字节数;-1 参数错;-2 3xx 重定向(Location 写 out_loc);
# -3 其它失败(last_error() 查看原因)。
# Large-file Range download primitive for requests.download. Thread-safe: no _mbuddy,
# independent SSL_CTX/SSL/socket per connection. Returns body bytes>=0; -1 arg; -2 redirect; -3 failure.
# ============================================================
def _DlOnce(url: str, start: sizet, count: INT, verify: bool, ca_file: str,
dst: str, dst_cap: INT, out_status: INT*,
out_loc: str, out_loccap: INT,
out_total: sizet*) -> INT:
"""HTTPS 单次 Range 拉取(线程安全,见模块注释)。"""
"""HTTPS single Range fetch (thread-safe; see module comment)."""
global _last_verify_val, _last_verify_flag
if url is None or dst is None or dst_cap <= 0 \
or out_status is None or out_total is None:
@@ -1005,7 +965,7 @@ def _DlOnce(url: str, start: sizet, count: INT, verify: bool, ca_file: str,
else:
_last_verify_val = -1
# 组 Range 请求头(不带 Accept-Encoding,保证响应为原始字节)
# Build Range request header (no Accept-Encoding, so response is raw bytes)
req: str = stdlib.malloc(1024)
if req is None:
openssl.SSL_free(ssl)
+9 -11
View File
@@ -2,9 +2,8 @@ import t
# =============================================================================
# 大写别名(Win32/TransPyC 风格)
# 注意:INTPTR/UINTPTR 在此模块中是 int*/unsigned int* 指针类型(历史命名)。
# 它们与 C 标准的 intptr_t/uintptr_t 整数类型不同,后者见下方小写 _t 区域。
# Uppercase aliases (Win32/TransPyC style)
# Note: INTPTR/UINTPTR are int*/unsigned int* pointer types here (historical), unlike the C intptr_t/uintptr_t integer types in the lowercase _t section below.
# =============================================================================
INT: t.CTypedef = int
INTPTR: t.CTypedef = int | t.CPtr
@@ -48,7 +47,7 @@ FLOAT128: t.CTypedef = t.CFloat128T
# =============================================================================
# 定长整数大写别名
# Fixed-width uppercase integer aliases
# =============================================================================
INT8: t.CTypedef = int8
INT16: t.CTypedef = int16
@@ -93,9 +92,8 @@ PTRDIFF_T: t.CTypedef = t.CPtrDiffT
# =============================================================================
# C 标准库 <stdint.h> 类型名(小写 _t 后缀)
# 这些是 C 语言标准类型名,从 _CNAME_ALIAS 挪到此处作为 typedef 提供。
# 用户 `from stdint import *` 后即可直接使用 int8_t / size_t 等。
# C standard library <stdint.h> type names (lowercase _t suffix)
# C standard type names provided here as typedefs; after `from stdint import *` you can use int8_t / size_t etc. directly.
# =============================================================================
int8_t: t.CTypedef = int8
int16_t: t.CTypedef = int16
@@ -111,18 +109,18 @@ size_t: t.CTypedef = sizet
ssize_t: t.CTypedef = t.CPtrDiffT
ptrdiff_t: t.CTypedef = t.CPtrDiffT
# intptr_t/uintptr_t 是足以容纳指针的整数类型(不是指针!)
# 与上方的 INTPTR/UINTPTR(int 指针)语义不同。
# intptr_t/uintptr_t are integer types large enough to hold a pointer (NOT pointers!)
# Different from the INTPTR/UINTPTR above (int pointers).
intptr_t: t.CTypedef = t.CIntPtrT
uintptr_t: t.CTypedef = t.CUIntPtrT
# 宽字符 / 字符类型
# Wide-character / character types
wchar_t: t.CTypedef = t.CWCharT
char8_t: t.CTypedef = t.CChar8T
char16_t: t.CTypedef = t.CChar16T
char32_t: t.CTypedef = t.CChar32T
# 浮点特化类型
# Floating-point specializations
float8_t: t.CTypedef = t.CFloat8T
float16_t: t.CTypedef = t.CFloat16T
float32_t: t.CTypedef = t.CFloat32T
+49 -76
View File
@@ -73,14 +73,11 @@ export def strlen(src: str) -> sizet:
return length
# String comparison function
# 注意:str1[0] - str2[0] 是 i8 减法会回绕(0xFF 表示 -1),返回前必须
# int() 提升到 i32 再相减(与下方 memcmp 的 int(p1[i]) - int(p2[i]) 一致),
# 否则 strcmp("abc","abd") 返回 255 而非 -1(StringTest Test 4 根因)。
# Note: str1[0] - str2[0] is an i8 subtraction that wraps (0xFF means -1), so both operands
# must be promoted to i32 before subtracting (as in memcmp below); otherwise strcmp("abc","abd") returns 255 instead of -1.
def strcmp(str1: str, str2: str) -> int:
# 指针相等短路(降单次成本,不降调用次数):查表/自比较场景常拿同一驻留
# 字符串与自身比较(如 HashTable 以同一指针 set/lookup),指针相同则内容
# 必相同,直接返回 0,省去整趟逐字节循环;指针不同则走下方原逻辑。
# 语义严格不变(相同指针 ⇒ 内容相同 ⇒ strcmp 结果本应为 0)。
# Pointer-equality short-circuit: identical pointers guarantee identical contents, so return 0 directly and skip the full byte loop.
# Semantics strictly unchanged (same pointer => same content => strcmp result is 0); different pointers fall through to the loop below.
if str1 == str2:
return 0
while str1[0] != '\0' and str2[0] != '\0':
@@ -96,8 +93,8 @@ def samestr(str1: str, str2: str) -> bool:
# String compare function with limited length
def strncmp(str1: str, str2: str, n: sizet) -> int:
# 指针相等短路(同 strcmp):相同指针在任意 n 下比较结果恒为 0(相同字符串
# 的前 n 个字符必然相等),直接返回 0,语义严格不变。
# Pointer-equality short-circuit (same as strcmp): identical pointers compare equal for any n, so return 0.
# Semantics strictly unchanged.
if str1 == str2:
return 0
i: sizet = 0
@@ -144,11 +141,8 @@ def strrchr(s: str, cr: int) -> str:
# Substring search function (find first occurrence of needle in s, return pointer to that position, or None if not found)
# 热点优化(实测):原实现对**每个字节位置**都调用一次 strncmp,并先对 s 做一次全文 strlen。
# 实测 strstr 被调用 918 万次、strncmp 因此被调用 7.44 亿次(占全程序调用 29%,是第一名)。
# 改为先用 strchr 跳到"首字符匹配"的候选位置(跳过绝大多数不可能匹配的字节),
# 只在候选点做整段比较;同时不再预先 strlen(s)(strncmp 本身遇 '\0' 即停,安全)。
# 语义与原实现完全一致:needle 为空返回 s;未找到返回 None。
# Hot-path optimization: use strchr to jump to first-char candidates instead of calling strncmp at every byte, and drop the upfront strlen(s) (strncmp stops at '\0').
# Semantics unchanged: an empty needle returns s; not found returns None.
def strstr(s: str, needle: str) -> str:
if needle[0] == 0:
return s
@@ -232,9 +226,9 @@ export def memcpy(dest: t.CPtr, src: t.CPtr, num: sizet) -> t.CPtr:
return dest
# 内存移动函数
# Memory move function
def memmove(dest: t.CPtr, src: t.CPtr, num: sizet) -> t.CPtr:
# 原始实现:
# Original implementation:
# d: BYTEPTR = BYTEPTR(dest)
# s: BYTEPTR = BYTEPTR(src)
# i: sizet
@@ -271,10 +265,10 @@ def atoi(src: str) -> int:
sign: int = 1
s: str = src
if s == None: return 0
# 跳过空白
# Skip whitespace
while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r':
s += 1
# 符号
# Sign
if s[0] == '-':
sign = -1
s += 1
@@ -299,7 +293,7 @@ def atoll(src: str) -> int64:
s += 1
elif s[0] == '+':
s += 1
# 十六进制前缀 0x / 0X
# Hex prefix 0x / 0X
is_hex: int = 0
if s[0] == '0' and (s[1] == 'x' or s[1] == 'X'):
is_hex = 1
@@ -327,26 +321,14 @@ def atoll(src: str) -> int64:
# ============================================================
# 十进制字面量 → double 的正确舍入转换(N4)
#
# 旧实现用"逐位累加 + 反复乘/除 10":小数位超过 16 位后每步都舍入
# (e 偏 +1 ULP、ln2 偏 −1 ULP),指数按 while exp>0: result*=10.0 累乘
# 308 次(DBL_MAX 偏 −6 ULP)。
# 新实现:尾数装进 u64(≤19 位;第 20 位做一次舍入),用 10 的**精确幂**
# 在**双 double(hi+lo,约 106 位)**下做一次缩放,最后一次性舍入。
# 算法先在 Python 里 1:1 对拍 float()(正确舍入)通过:20 万随机样例 +
# 边界样例在**正规数范围**内 bit-exact;次正规/溢出仍走旧逐步缩放兜底
# (语义与旧实现一致,无回归)。
# 约定:不用全局表——10^0..10^22 由循环乘 10 得到(该区间每一步都可精确
# 表示);p<0 时在正规区算 10^p×2^600 再精确缩回,避免双 double 低位在
# ~1e-292 以下掉进次正规而丢有效位。
# Correctly-rounded decimal literal -> double conversion (N4): mantissa in u64 (<=19 digits, 20th digit rounds once),
# scaled once by an exact power of ten in double-double (hi+lo, ~106 bits) then rounded a single final time.
# Convention: no global table (10^0..10^22 built by repeated multiplication of 10, each step exact).
# For p<0 compute 10^p*2^600 in the normal range and scale back exactly, avoiding subnormal loss in the double-double low part below ~1e-292.
# ============================================================
# _dd_split_hi - Dekker 分裂的上半(hi = 取高 26 位)
#
# 含量级缩尺:|a| > 6.7e292 时先乘 2^-28 再算(否则 a*(2^27+1) 溢出为 inf);
# |a| 极小(< 1e-292)时先乘 2^28(否则 lo = a-hi 掉进次正规丢位)。
# 缩尺因子均为 2 的幂 ⇒ 对正规数**无损**;调用方用 lo = a - hi 取下半部分。
# _dd_split_hi - upper half of the Dekker split (hi = top 26 bits).
# Magnitude rescaling: multiply by 2^-28 when |a| > 6.7e292 and by 2^28 when |a| < 1e-292 (powers of two, lossless for normals); caller gets the lower half via lo = a - hi.
def _dd_split_hi(a: double) -> double:
aa: double = a
big: int = 0
@@ -366,7 +348,7 @@ def _dd_split_hi(a: double) -> double:
return hi
# _dd_mul - 双 double 乘法:out = a × b(out[0]=hi, out[1]=lo)
# _dd_mul - double-double multiply: out = a × b (out[0]=hi, out[1]=lo)
def _dd_mul(ah: double, al: double, bh: double, bl: double,
out: t.CArray[double, 2]):
p: double = ah * bh
@@ -381,7 +363,7 @@ def _dd_mul(ah: double, al: double, bh: double, bl: double,
out[1] = e - (s - p)
# _dd_add - 双 double 加法:out = a + b
# _dd_add - double-double add: out = a + b
def _dd_add(ah: double, al: double, bh: double, bl: double,
out: t.CArray[double, 2]):
s: double = ah + bh
@@ -393,7 +375,7 @@ def _dd_add(ah: double, al: double, bh: double, bl: double,
out[1] = e - (r0 - s)
# _dd_div - 双 double 除法:out = a / b(q0 + 一步残差修正)
# _dd_div - double-double divide: out = a / b (q0 plus one residual correction step)
def _dd_div(ah: double, al: double, bh: double, bl: double,
out: t.CArray[double, 2], tmp: t.CArray[double, 2]):
q0: double = ah / bh
@@ -403,7 +385,7 @@ def _dd_div(ah: double, al: double, bh: double, bl: double,
_dd_add(q0, 0.0, q1, 0.0, out)
# _dd_from_u64 - u64 → 双 double(精确:hi 为四舍五入值,lo 为精确残差)
# _dd_from_u64 - u64 -> double-double (exact: hi is the rounded value, lo the exact residual)
def _dd_from_u64(m: uint64, out: t.CArray[double, 2]):
hi: double = double(m)
lo: double = 0.0
@@ -415,7 +397,7 @@ def _dd_from_u64(m: uint64, out: t.CArray[double, 2]):
out[1] = (hi - (s - bb)) + (lo - bb)
# _dd_pow10 - 10^e 的双 double(e >= 0;按 ≤22 分块,每块可精确表示)
# _dd_pow10 - double-double 10^e (e >= 0; chunked by <=22 so each chunk is exact)
def _dd_pow10(e: int, out: t.CArray[double, 2], tmp: t.CArray[double, 2]):
out[0] = 1.0
out[1] = 0.0
@@ -433,7 +415,7 @@ def _dd_pow10(e: int, out: t.CArray[double, 2], tmp: t.CArray[double, 2]):
k -= step
# _pow2 - 2^n(|n| <= 1000):循环乘 2/0.5,每步精确
# _pow2 - 2^n (|n| <= 1000): repeated multiply by 2/0.5, exact at every step
def _pow2(n: int) -> double:
r: double = 1.0
i: int = n
@@ -448,7 +430,7 @@ def _pow2(n: int) -> double:
return r
# _atof_scale_stepwise - 旧的逐步 ×/÷10 缩放(次正规/溢出场景的兜底)
# _atof_scale_stepwise - stepwise ×/÷10 scaling (fallback for subnormal/overflow cases)
def _atof_scale_stepwise(x: double, p: int) -> double:
r: double = x
e: int = p
@@ -464,17 +446,17 @@ def _atof_scale_stepwise(x: double, p: int) -> double:
def atof(src: str) -> double:
s: str = src
if s == None: return 0.0
# 跳过空白
# Skip whitespace
while s[0] == ' ' or s[0] == '\t' or s[0] == '\n' or s[0] == '\r':
s += 1
# 符号
# Sign
sign: double = 1.0
if s[0] == '-':
sign = -1.0
s += 1
elif s[0] == '+':
s += 1
# 尾数:有效数字装进 u64(≤19 位),同时维护十进制指数
# Mantissa: significant digits packed into u64 (<=19 digits), tracking the decimal exponent
mant: uint64 = 0
nd: int = 0
dexp: int = 0
@@ -486,7 +468,7 @@ def atof(src: str) -> double:
if ch >= '0' and ch <= '9':
d: int = ch - '0'
if started == 0:
# 前导零不进尾数;小数部分的前导零要占十进制位
# Leading zeros are not stored in the mantissa; leading zeros in the fraction still advance the decimal exponent
if d != 0:
started = 1
mant = uint64(d)
@@ -501,8 +483,8 @@ def atof(src: str) -> double:
if seen_dot != 0:
dexp -= 1
else:
# 第 20 位起:整数部分丢弃的位让指数 +1;小数部分丢弃的位不影响
# 指数。只用第 20 位做一次舍入(丢弃量 <= 10^-19 相对误差)。
# From the 20th digit on: dropped integer digits bump the exponent; dropped fraction digits do not.
# Only the 20th digit is used for one rounding (dropped amount <= 1e-19 relative error).
if seen_dot == 0:
dexp += 1
elif rounded == 0:
@@ -516,11 +498,11 @@ def atof(src: str) -> double:
s += 1
if started == 0:
return sign * 0.0
# 进位:舍入可能使 19 位变 20 位
# Carry: rounding may turn 19 digits into 20
if mant >= uint64(10000000000000000000):
mant = mant // uint64(10)
dexp += 1
# 指数部分
# Exponent part
exp: int = 0
if s[0] == 'e' or s[0] == 'E':
s += 1
@@ -537,7 +519,7 @@ def atof(src: str) -> double:
p: int = dexp + exp
if p == 0:
return sign * double(mant)
# 双 double 缩放:mant × 10^p,最后一次舍入
# Double-double scaling: mant × 10^p, single final rounding
ta: t.CArray[double, 2]
tb: t.CArray[double, 2]
tc: t.CArray[double, 2]
@@ -549,9 +531,8 @@ def atof(src: str) -> double:
_dd_mul(ta[0], ta[1], tb[0], tb[1], tr)
v = tr[0] + tr[1]
else:
# p < 0:在正规区算「2^600 / 10^(-p)」= 10^p × 2^600,最后精确缩回。
# 不能直接算 10^p(|p| 大时 ~1e-308,双 double 低位掉次正规丢有效位);
# 分母按 ≤308 分块累除(10^313 会先溢出成 inf 使结果变 NaN)。
# p < 0: compute 2^600 / 10^(-p) (= 10^p * 2^600) in the normal range, then scale back exactly.
# Direct 10^p loses effective bits to subnormals for large |p|; divide the denominator in chunks of <=308 (10^313 overflows to inf and yields NaN).
tb[0] = _pow2(600)
tb[1] = 0.0
q: int = -p
@@ -566,20 +547,17 @@ def atof(src: str) -> double:
q -= step2
_dd_mul(ta[0], ta[1], tb[0], tb[1], tr)
v = (tr[0] + tr[1]) * _pow2(-600)
# 正规数范围(非 NaN / 非 inf / 非次正规)→ 已正确舍入的结果
# Normal range (not NaN / not inf / not subnormal) -> the correctly rounded result
if v == v and v <= 1.7976931348623157e308 and v >= -1.7976931348623157e308 \
and (v == 0.0 or v >= 2.2250738585072014e-308 or v <= -2.2250738585072014e-308):
return sign * v
# 溢出 / 次正规 / NaN → 回退旧逐步缩放(与旧实现语义一致)
# Overflow / subnormal / NaN -> fall back to stepwise scaling
return sign * _atof_scale_stepwise(double(mant), p)
def atof_out(src: str, out: double*) -> int:
"""atof 结果写入 out[0](输出参数方式,避免跨模块 double 返回值传递 bug)
返回值仅表示成功与否(1=成功,0=失败),结果值通过 out 指针传递,
不依赖 double 返回值的跨模块传递。
"""
"""Write the atof result to out[0] (out-parameter form, avoiding the cross-module double return bug).
The return value only signals success (1) or failure (0); the value is passed via the out pointer."""
if out is None:
return 0
v: double = atof(src)
@@ -587,39 +565,34 @@ def atof_out(src: str, out: double*) -> int:
return 1
def split(s: str, delim: str, result: t.CArray[str], cap: int) -> int:
"""按单字符分隔符切分 s,token 指针写入 result[0..],尾部写 None 哨兵。
cap: result 数组的**声明容量**(元素个数)。此前无容量参数 → 写
result[count] 与 result[count]=None 均无上界;token 数超过调用方数组
容量时越界写(devfs 用 t.CArray[str, 256],256 个 token 即写穿 2048 字节)。
现限制 token 数 ≤ cap-1(留一格给结尾哨兵)。
"""
"""Split s by a single-character delimiter, writing token pointers to result[0..] and a None sentinel at the end.
cap is the declared capacity (element count); token count is limited to <= cap-1, leaving one slot for the sentinel."""
count: int = 0
start: str = s
if not s or not delim or not result:
return 0
if cap <= 0:
return 0
d: char = delim[0] # 单分隔符
d: char = delim[0] # single delimiter
while start[0]:
# 跳过分隔符
# Skip delimiters
while start[0] == d: start += 1
if not start[0]: break
# 容量保护:留最后一格放结尾哨兵
# Capacity guard: keep the last slot for the end sentinel
if count >= cap - 1:
break
# 记录token起始
# Record token start
result[count] = start
count += 1
# 找到下一个分隔符,截断
# Find the next delimiter and truncate
while start[0] and start[0] != d: start += 1
if start[0]:
start[0] = '\0'
start += 1
result[count] = None # 结尾标记
result[count] = None # end marker
return count
+2 -2
View File
@@ -40,7 +40,7 @@ def platform() -> str:
# Process Information
# =============================================================================
def get_pid() -> int:
"""返回当前进程 ID"""
"""Return the current process ID"""
if platmacro.IS_WINDOWS: return w32.win32process.GetCurrentProcessId()
return posix.getpid()
@@ -57,7 +57,7 @@ def _init_argv():
"""Initialize global argv once on first access"""
global _argc, _argv
if _argc != 0: return
# 必须用 if/else 派发:编译器只翻译"调用表达式语句",三元表达式语句会被丢弃(argv 恒空)
# Must dispatch with if/else: the compiler only translates call-expression statements; a ternary statement is dropped (argv stays empty)
if platmacro.IS_WINDOWS:
_init_argv_win32()
else:
+57 -85
View File
@@ -30,17 +30,17 @@ import memhub
# Tensor
# ------------------------------------------------------------
novirtual class Tensor:
Data: t.CPtr # rows*cols*8 字节连续 double
Grad: t.CPtr # 同尺寸梯度(可为 None)
Data: t.CPtr # rows*cols*8 contiguous bytes of double
Grad: t.CPtr # same-shape gradient (may be None)
Rows: sizet
Cols: sizet
# 字段: Data/Grad/Rows/Cols = 32 字节(无 vtable)
# Fields Data/Grad/Rows/Cols = 32 bytes (no vtable)
define TENSOR_SIZE = 32
def _zeros_block(pool: memhub.MemBuddy*, bytes_n: sizet) -> t.CPtr:
"""分配并清零一块内存"""
"""Allocate and zero a block of memory"""
p: t.CPtr = pool.alloc(bytes_n)
if p is not None:
string.memset(p, 0, bytes_n)
@@ -49,7 +49,7 @@ def _zeros_block(pool: memhub.MemBuddy*, bytes_n: sizet) -> t.CPtr:
def zeros(pool: memhub.MemBuddy*, rows: sizet,
cols: sizet, with_grad: int = 1) -> Tensor*:
"""torch.zeros: 创建 rows x cols 的全零 Tensor, 可选分配 Grad 缓冲"""
"""torch.zeros: create a rows x cols all-zero Tensor, optionally allocating a Grad buffer"""
if pool is None:
return None
t0: rawptr[Tensor] = pool.alloc(TENSOR_SIZE)
@@ -70,7 +70,7 @@ def zeros(pool: memhub.MemBuddy*, rows: sizet,
def from_raw(pool: memhub.MemBuddy*, data: t.CPtr,
rows: sizet, cols: sizet,
with_grad: int = 0) -> Tensor*:
"""torch.from_raw: 包装一块已有内存为 Tensor(不复制, 不拥有)"""
"""torch.from_raw: wrap existing memory as a Tensor (no copy, not owned)"""
if pool is None:
return None
t0: rawptr[Tensor] = pool.alloc(TENSOR_SIZE)
@@ -85,7 +85,7 @@ def from_raw(pool: memhub.MemBuddy*, data: t.CPtr,
def tensor_free(t0: Tensor*):
"""释放 Tensor 外壳(数据由 MemBuddy 池统一回收, 无需手动 free)"""
"""Free the Tensor shell (data is reclaimed by the MemBuddy pool; no manual free needed)"""
pass
@@ -114,7 +114,7 @@ def tensor_cols(t0: Tensor*) -> sizet:
def fill(t0: Tensor*, val: double):
"""torch.fill: 全部元素置为 val"""
"""torch.fill: set all elements to val"""
if t0 is None or t0.Data is None:
return
d: double* = t0.Data
@@ -124,14 +124,14 @@ def fill(t0: Tensor*, val: double):
def zero_grad(t0: Tensor*):
"""torch.zero_grad: Grad 缓冲清零"""
"""torch.zero_grad: zero the Grad buffer"""
if t0 is None or t0.Grad is None:
return
string.memset(t0.Grad, 0, t0.Rows * t0.Cols * sizet(8))
# ------------------------------------------------------------
# 确定性伪随机(LCG, minstd)与 Kaiming 均匀初始化
# Deterministic pseudo-random (LCG, minstd) and Kaiming uniform init
# ------------------------------------------------------------
_rng_state: double = 12345.0
LCG_M: double = 2147483647.0
@@ -146,14 +146,14 @@ def manual_seed(seed: int64):
def _next_rand() -> double:
"""(0,1) 均匀随机"""
"""(0,1) uniform random"""
global _rng_state
_rng_state = math.fmod(_rng_state * LCG_A, LCG_M)
return _rng_state / LCG_M
def uniform_fill(t0: Tensor*, lo: double, hi: double):
"""U(lo, hi) 填充"""
"""Fill with U(lo, hi)"""
if t0 is None or t0.Data is None:
return
d: double* = t0.Data
@@ -165,18 +165,18 @@ def uniform_fill(t0: Tensor*, lo: double, hi: double):
def kaiming_uniform_fill(w: Tensor*, fan_in: sizet,
fan_out: sizet):
"""PyTorch Linear 默认初始化: U(-sqrt(6/(fan_in+fan_out)), +...)"""
"""PyTorch Linear default init: U(-sqrt(6/(fan_in+fan_out)), +...)"""
bound: double = math.sqrt(
double(6.0) / double(fan_in + fan_out))
uniform_fill(w, -bound, bound)
# ------------------------------------------------------------
# 基础元素级运算(结合 vipersimd 4 宽指令)
# Basic element-wise ops (using vipersimd 4-wide instructions)
# ------------------------------------------------------------
def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr,
n: sizet):
"""dst = src * scalar (逐 4 元素 simd_mul_scalar4d + 标量尾数)"""
"""dst = src * scalar (4-wide simd_mul_scalar4d + scalar tail)"""
if src is None or scalar_ptr is None or dst is None:
return
i: sizet = 0
@@ -188,7 +188,7 @@ def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr,
i += 4
sd: double* = src
dd: double* = dst
# 尾部标量
# Scalar tail
sp: double* = scalar_ptr
sval: double = sp[0]
while i < n:
@@ -197,7 +197,7 @@ def vector_mul_scalar4(src: t.CPtr, scalar_ptr: t.CPtr, dst: t.CPtr,
def vector_add_inplace(a: t.CPtr, b: t.CPtr, n: sizet):
"""a += b (逐 4 元素 simd_add_array 尾部标量)"""
"""a += b (4-wide simd_add_array + scalar tail)"""
if a is None or b is None:
return
tmp: t.CPtr = stdlib.malloc(8)
@@ -219,8 +219,8 @@ def vector_add_inplace(a: t.CPtr, b: t.CPtr, n: sizet):
# ------------------------------------------------------------
# 线性层: forward = x @ W^T + b (W: out x in, x: m x in, out: m x out)
# 内积按 k%4 用 vipersimd.simd_dot4d —— MNIST 主计算热点
# Linear layer: forward = x @ W^T + b (W: out x in, x: m x in, out: m x out)
# Inner product in k%4 chunks via vipersimd.simd_dot4d -- the MNIST compute hotspot
# ------------------------------------------------------------
def linear_fwd(w: Tensor*, b: Tensor*,
x: Tensor*, out: Tensor*):
@@ -258,10 +258,10 @@ def linear_fwd(w: Tensor*, b: Tensor*,
# ------------------------------------------------------------
# 线性层反向: 输入 w(out,in) x(m,in) g(m,out)
# Linear backward: inputs w(out,in) x(m,in) g(m,out)
# dw[j,i] += sum_b g[b,j]*x[b,i] ; db[j] += sum_b g[b,j]
# dx[b,i] = sum_j g[b,j]*w[j,i]
# 注意 dw/db 按"累加"语义(调用前清零 Grad)
# Note: dw/db use accumulate semantics (zero Grad before the call)
# ------------------------------------------------------------
def linear_bwd(w: Tensor*, x: Tensor*,
g: Tensor*, dw: Tensor*,
@@ -281,8 +281,8 @@ def linear_bwd(w: Tensor*, x: Tensor*,
dbd: double* = db.Data
dxd: double* = dx.Data
# dw/db/dx 清零(dx 恒清零: 它是本样本的输入梯度, 不跨样本累积)
# accum=1 时保留 dw/db 现值 —— 逐样本反向累积整批参数梯度的调用方用
# Zero dw/db/dx (dx is always zeroed: it is this sample's input gradient, not accumulated across samples)
# accum=1 keeps dw/db -- for callers accumulating whole-batch parameter gradients sample by sample
if accum == 0:
string.memset(dw.Data, 0, n * k * sizet(8))
string.memset(db.Data, 0, n * sizet(8))
@@ -303,7 +303,7 @@ def linear_bwd(w: Tensor*, x: Tensor*,
# ------------------------------------------------------------
# ReLU (simd_max4d 版本) 与反向
# ReLU (simd_max4d version) and backward
# ------------------------------------------------------------
def relu_fwd(x: Tensor*, out: Tensor*):
if x is None or out is None:
@@ -348,9 +348,9 @@ def relu_backward(x: Tensor*, g: Tensor*,
# ------------------------------------------------------------
# softmax + CrossEntropy(融合, 数值稳定) —— MNIST 输出层
# logits: (m, C), labels: m 个 int (0..C-1)
# loss 写入 *loss, 梯度(已按 1/m 归一)写入 grad (m, C)
# softmax + CrossEntropy (fused, numerically stable) -- MNIST output layer
# logits: (m, C), labels: m ints (0..C-1)
# loss written to *loss, gradient (normalized by 1/m) written to grad (m, C)
# ------------------------------------------------------------
def softmax_crossentropy(logits: Tensor*,
labels: int*,
@@ -369,7 +369,7 @@ def softmax_crossentropy(logits: Tensor*,
total: double = double(0.0)
inv_m: double = double(1.0) / double(m)
for b in range(m):
# 数值稳定 softmax
# Numerically stable softmax
mx: double = ld[b * c]
j: sizet = 1
while j < c:
@@ -384,7 +384,7 @@ def softmax_crossentropy(logits: Tensor*,
y: int = lb[b]
# loss += -(logits[lab] - max - log(s))
total = total - (ld[b * c + sizet(y)] - mx - math.log(s))
# 梯度: (softmax - onehot) / m
# Gradient: (softmax - onehot) / m
j = 0
while j < c:
p: double = math.exp(ld[b * c + j] - mx) / s
@@ -397,7 +397,7 @@ def softmax_crossentropy(logits: Tensor*,
# ------------------------------------------------------------
# 每行 argmax (分类预测)
# Per-row argmax (classification prediction)
# ------------------------------------------------------------
def row_argmax(x: Tensor*, pred: int*):
if x is None or pred is None:
@@ -418,7 +418,7 @@ def row_argmax(x: Tensor*, pred: int*):
# ------------------------------------------------------------
# SGD: data -= lr * grad(全量梯度) / 重球法: v = m*v+g, data -= lr*v
# SGD: data -= lr * grad (full gradient) / momentum: v = m*v+g, data -= lr*v
# ------------------------------------------------------------
def sgd_step(param: Tensor*, lr: double):
if param is None or param.Data is None or param.Grad is None:
@@ -432,11 +432,8 @@ def sgd_step(param: Tensor*, lr: double):
def sgd_step_momentum(param: Tensor*, vel: t.CPtr,
lr: double, momentum: double):
"""带动量(重球法)的 SGD: v = m*v + g ; w -= lr*v
vel 是与 param 同元素个数的 double 缓冲, 由调用方分配并清零(跨 step 持有);
vel 传 None 时退化为普通 sgd_step。动量是 CNN/MLP 训练收敛速度的关键 ——
纯 SGD 在固定 lr 下前几个 epoch 很慢且容易卡在低准确率。"""
"""SGD with momentum: v = m*v + g ; w -= lr*v
vel is a caller-allocated, zeroed double buffer with the same element count as param (held across steps); pass None to fall back to plain sgd_step."""
if param is None or param.Data is None or param.Grad is None:
return
if vel is None:
@@ -453,7 +450,7 @@ def sgd_step_momentum(param: Tensor*, vel: t.CPtr,
# ------------------------------------------------------------
# 逐元素乘加 (调试/扩展用, vipersimd fma_array)
# Element-wise multiply-add (for debug/extension, vipersimd fma_array)
# a = a*b + c
# ------------------------------------------------------------
def fma_array_inplace(a: Tensor*, b: t.CPtr, c: t.CPtr):
@@ -463,31 +460,13 @@ def fma_array_inplace(a: Tensor*, b: t.CPtr, c: t.CPtr):
# ------------------------------------------------------------
# Conv2d / MaxPool2d —— 轻量 CNN 用的原始缓冲算子
#
# 这些算子直接吃裸缓冲(不用 Tensor: torch.Tensor 只有 2D 形状),
# 布局一律 NCHW 连续 double; 卷积权重 [cout][cin][k][k]。
# 约定:
# *_fwd 覆盖写输出
# conv2d_bwd_dx 先清零再累加(逐样本输入梯度, 不跨样本累积)
# maxpool2d_bwd 同上
# conv2d_bwd_dw += 累加参数梯度(跨样本累积, 批次开始由调用方清零)
#
# SIMD: 三个卷积算子的内层都按"输出列"做 4 路 AVX2 double。
# 依据是 stride==1 时相邻输出列恰好对应相邻输入列, 故一列 4 个 double
# 在内存里连续, 可直接 vmovupd + vfmadd213pd, 权重标量 vbroadcastsd。
# 代价是需要一个"安全列区间": 4 条 lane 的输出列都在 [0,wout) 内, 且它们
# 触及的输入列 [ox-pad, ox+3+k-1-pad] 全在 [0,win) 内 —— 即
# ox ∈ [pad, win+pad-k-3]。区间外的列与 stride!=1 一律走原标量分支。
# (maxpool2d 不做 SIMD: k==stride==2 时 4 个窗口起点间隔 2 个 double,
# 不是连续地址, 无 gather 原语; 且它占总计算量不到 1%。)
# Conv2d / MaxPool2d raw-buffer kernels for a lightweight CNN (NCHW contiguous double, weights [cout][cin][k][k]).
# Conventions: *_fwd overwrites output; conv2d_bwd_dx/maxpool2d_bwd zero then accumulate (per-sample dx); conv2d_bwd_dw accumulates params (+=, caller zeroes per batch).
# SIMD inner loop over output columns, safe range ox in [pad, win+pad-k-3] when stride==1 (maxpool2d is scalar only).
# ------------------------------------------------------------
def _dptr(base: t.CPtr, elem: sizet) -> t.CPtr:
"""裸 double 缓冲的元素下标 → 字节地址
裸 t.CPtr 是 i8*, `p + n` 走的是字节偏移; 而 vipersimd 的 4 路原语按
"4 个 double = 32 字节"读写, 所以下标必须显式 ×8。
(与 linear_fwd 里的 t.CPtr(uint64(p) + i * 8) 同一写法。)"""
"""Convert an element index into a byte address for a raw double buffer.
Raw t.CPtr is i8* and `p + n` is a byte offset, so the index must be multiplied by 8 for vipersimd 4-wide (32-byte) access."""
return t.CPtr(uint64(base) + uint64(elem) * 8)
@@ -496,7 +475,7 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr,
cout: sizet, k: sizet, stride: sizet,
pad: sizet):
"""out[n][cout][hout][wout] = conv2d(x, w) + b
hout = (hin + 2*pad - k)/stride + 1(wout 同理, 整除向下取整)"""
hout = (hin + 2*pad - k)/stride + 1 (same for wout; integer division floors)."""
if x is None or w is None or out is None:
return
hout: sizet = (hin + pad * 2 - k) / stride + 1
@@ -505,14 +484,14 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr,
wd: double* = w
od: double* = out
kk: sizet = cin * k * k
# SIMD 安全区间(见文件头说明)
# SIMD safe range (see file-header notes)
simd_on: int = 0
smax: sizet = 0
if stride == 1 and win + pad >= k + 3:
simd_on = 1
smax = win + pad - (k + 3)
wb: t.CPtr = None # 权重标量 × 4 路广播副本(kk 个标量)
t4: t.CPtr = None # 4 路累加器
wb: t.CPtr = None # 4-lane broadcast copy of weight scalars (kk scalars)
t4: t.CPtr = None # 4-lane accumulator
if simd_on != 0:
wb = stdlib.malloc(kk * 32)
t4 = stdlib.malloc(32)
@@ -534,9 +513,8 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr,
if b is not None:
bd: double* = b
bias = bd[co]
# 权重标量预广播: 每个 (ci,ky,kx) 一个 4 路副本, 本通道所有输出
# 行/列共用。否则 vbroadcastsd(4 次 store) 会跟着 4 路循环重复,
# 把 SIMD 的收益吃掉一半。
# Pre-broadcast weight scalars: one 4-lane copy per (ci,ky,kx), shared by all output rows/cols of this channel.
# Otherwise vbroadcastsd (4 stores) repeats in the 4-lane loop and halves the SIMD gain.
if simd_on != 0:
wb4: sizet = 0
while wb4 < kk:
@@ -552,7 +530,7 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr,
ox: sizet = 0
while ox < wout:
if simd_on != 0 and ox >= pad and ox <= smax and ox + 4 <= wout:
# ---- 4 路: 一次算 4 个相邻输出列 ----
# ---- 4-lane: compute 4 adjacent output columns at once ----
t4d[0] = bias
t4d[1] = bias
t4d[2] = bias
@@ -582,7 +560,7 @@ def conv2d_fwd(x: t.CPtr, w: t.CPtr, b: t.CPtr, out: t.CPtr,
od[ob + 3] = t4d[3]
ox += 4
else:
# ---- 标量: 边界列 / stride != 1 ----
# ---- scalar: boundary columns / stride != 1 ----
ox0: sizet = ox * stride
acc: double = bias
ci2: sizet = 0
@@ -619,11 +597,8 @@ def conv2d_bwd_dw(x: t.CPtr, dout: t.CPtr, dw: t.CPtr, db: t.CPtr,
n: sizet, cin: sizet, hin: sizet, win: sizet,
cout: sizet, k: sizet, stride: sizet,
pad: sizet):
"""累加: dw[co][ci][ky][kx] += sum dout*x ; db[co] += sum dout
(+= 语义, 调用方在批次开始前清零)
循环顺序为 (ci,ky,kx) 在外、(oy,ox) 在内: 每个权重元素只做一次
读-改-写; 内层按输出列 4 路 SIMD 累加, 最后 hsum 成标量。"""
"""Accumulate: dw[co][ci][ky][kx] += sum dout*x ; db[co] += sum dout (+= semantics, caller zeroes before the batch).
Loop order (ci,ky,kx) outer, (oy,ox) inner: one read-modify-write per weight; inner loop 4-lane SIMD, then hsum to scalar."""
if x is None or dout is None or dw is None:
return
hout: sizet = (hin + pad * 2 - k) / stride + 1
@@ -651,7 +626,7 @@ def conv2d_bwd_dw(x: t.CPtr, dout: t.CPtr, dw: t.CPtr, db: t.CPtr,
co: sizet = 0
while co < cout:
gp: sizet = (bi * cout + co) * hout * wout
# ---- db[co] += sum(g): g 的整个通道平面是连续的 ----
# ---- db[co] += sum(g): the whole channel plane of g is contiguous ----
if dbd is not None:
tot0: double = double(0.0)
if simd_on != 0:
@@ -732,10 +707,8 @@ def conv2d_bwd_dx(w: t.CPtr, dout: t.CPtr, dx: t.CPtr,
n: sizet, cin: sizet, hin: sizet, win: sizet,
cout: sizet, k: sizet, stride: sizet,
pad: sizet):
"""dx 先清零再累加(逐样本输入梯度): dx[ci][iy][ix] += sum dout*w
stride==1 时内层按输出列 4 路 SIMD: 4 条 lane 对应的 dx 单元也是连续的,
故可直接对 dx 做 in-place fma(g4 * w_broadcast + dx4)。"""
"""dx is zeroed then accumulated (per-sample input gradient): dx[ci][iy][ix] += sum dout*w.
With stride==1 the inner loop is 4-lane SIMD; the lanes map to contiguous dx cells, so dx can be updated in place with fma(g4 * w4 + dx4)."""
if w is None or dout is None or dx is None:
return
hout: sizet = (hin + pad * 2 - k) / stride + 1
@@ -750,15 +723,14 @@ def conv2d_bwd_dx(w: t.CPtr, dout: t.CPtr, dx: t.CPtr,
if stride == 1 and win + pad >= k + 3:
simd_on = 1
smax = win + pad - (k + 3)
wb: t.CPtr = None # 整个权重张量的 4 路广播副本(cout*kk 个标量)
wb: t.CPtr = None # 4-lane broadcast copy of the whole weight tensor (cout*kk scalars)
if simd_on != 0:
wb = stdlib.malloc(cout * kk * 32)
if wb is None:
simd_on = 0
wbd: double* = wb
if simd_on != 0:
# 权重在一次调用内不变 → 整体预广播一次(注意别在 4 路循环里重复
# vbroadcastsd: 那是 4 次 store, 会把 SIMD 的收益吃掉一半)
# Weights are constant within one call, so pre-broadcast once (do not repeat vbroadcastsd in the 4-lane loop: 4 stores halves the SIMD gain)
wjn: sizet = cout * kk
for wj4 in range(wjn):
wq2: double = wd[wj4]
@@ -819,12 +791,12 @@ def conv2d_bwd_dx(w: t.CPtr, dout: t.CPtr, dx: t.CPtr,
# ------------------------------------------------------------
# MaxPool2d (k x k, stride 通常 = k)
# MaxPool2d (k x k, stride usually = k)
# ------------------------------------------------------------
def maxpool2d_fwd(x: t.CPtr, out: t.CPtr, argmax: t.CPtr,
n: sizet, c: sizet, hin: sizet, win: sizet,
k: sizet, stride: sizet):
"""argmax: int 数组 [n*c*hout*wout], 记窗口内最大值在本通道内的下标(yy*win+xx)"""
"""argmax: int array [n*c*hout*wout] recording the max index within the channel (yy*win+xx)"""
if x is None or out is None or argmax is None:
return
hout: sizet = (hin - k) / stride + 1
@@ -869,7 +841,7 @@ def maxpool2d_fwd(x: t.CPtr, out: t.CPtr, argmax: t.CPtr,
def maxpool2d_bwd(dout: t.CPtr, argmax: t.CPtr, dx: t.CPtr,
n: sizet, c: sizet, hin: sizet, win: sizet,
k: sizet, stride: sizet):
"""dx 先清零再散射累加: dx[argmax] += dout"""
"""dx is zeroed then scatter-accumulated: dx[argmax] += dout"""
if dout is None or argmax is None or dx is None:
return
hout: sizet = (hin - k) / stride + 1
+6 -10
View File
@@ -433,17 +433,13 @@ import w32.win32dbghelp as dbg
extern def GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: CHARPTR, nSize: DWORD) -> DWORD
# ---- 编译期开关 -------------------------------------------------------------
# ---- Compile-time switch -------------------------------------------------------------
# 1 = 允许在**可选加强**路径上使用 dbghelp!StackWalk64(**产物必须链接 -ldbghelp**);
# 注意:主回溯路径是 WalkRbpChain(零 dbghelp 依赖),dbghelp 只在 RBP 链拿不到更多帧
# 且 Install 探针判定其可用时才补一次——ASan 只是与它**共享** DbgHelp 状态,不再一票
# 否决(见模块头第十一节 B)。
# 0 = 完全不用 dbghelp。切换机制:常量比较在 HandleIf 处被常量折叠,凡出现在
# `if TRACEBACK_USE_DBGHELP != 0:` 分支里的语句(Install 的 SymInitialize/GetProcAddress
# 段、WalkViaDbgHelp 的整个函数体、DumpMainThread 的可选加强调用)在置 0 时整体不生成代码
# ——范式与依据同 includes/memhub.vp 的 MEMBUDDY_THREAD_SAFE 注释("未取分支整体不生成
# 代码 ⇒ 函数体为空")。故置 0 后本模块不留任何 dbghelp 链接期符号。
# 1 = allow dbghelp!StackWalk64 on the optional-augmentation path (artifacts MUST link -ldbghelp);
# the main path is WalkRbpChain (no dbghelp) and dbghelp is only added when the RBP chain yields no
# more frames and the Install probe passes; 0 = no dbghelp at all, and constant folding removes the
# whole `if TRACEBACK_USE_DBGHELP != 0:` branch (same mechanism as MEMBUDDY_THREAD_SAFE in
# includes/memhub.vp), leaving no dbghelp link-time symbols.
define TRACEBACK_USE_DBGHELP = 1
# Trigger event values: see includes/w32/win32console.vp (CTRL_C_EVENT=0 / CTRL_BREAK_EVENT=1).
+1 -1
View File
@@ -28,7 +28,7 @@ class Buf:
for i in range(n):
self.data[self.length + i] = src[i]
self.length += n
# 恰好写满(n==avail)时 data[capacity] 越界 1 字节,加保护
# When exactly full (n==avail), data[capacity] would overflow by 1 byte, so guard it
if self.length < self.capacity: self.data[self.length] = 0
return n
+14 -21
View File
@@ -13,8 +13,8 @@ def get_digit_count(num: uint, base: int) -> int:
num //= base
return count
# 返回类型必须与 CRT `sprintf` 以及 includes/stdio.vp 的
# 语义上直接返回 vsnprintf 的写入长度(与 CRT 一致)。
# Return type must be int, matching the CRT `sprintf` and includes/stdio.vp;
# return vsnprintf's write length directly (consistent with the CRT).
export def sprintf(buf: str, rule: str, *args) -> int:
if buf is None or rule is None: return 0
return vsnprintf(buf, 0x7FFFFFFF, rule, args)
@@ -29,14 +29,8 @@ export def snprintf(buf: str, size: sizet, fmt: str, *args) -> int:
ret: int = vsnprintf(buf, size, fmt, args)
return ret
# 检查浮点特殊值(IEEE 754)
# 返回:0=正常, 1=+inf, 2=-inf, 3=nan
# 注意:不可用位双关 `*&fbits = *&fv` 读取 IEEE 位模式——该赋值会被编译器静默
# 丢弃(IR 中 fbits 恒为 0,见 TransPyV/output/db514c1af999c5f5.text.ll),会使
# 本函数恒返回 0、特殊值检查彻底失效,进而让 inf 进入浮点格式化循环而死循环。
# 这里改用纯浮点语义判定,不依赖任何位操作:
# NaN :fv != fv 为真
# ±inf:fv - fv 得到 NaN(有限值得到 0.0)
# Check float special values (IEEE 754): 0=normal, 1=+inf, 2=-inf, 3=nan.
# Do NOT read the bit pattern via `*&fbits = *&fv`: the assignment is silently dropped (fbits stays 0), so this function would always return 0 and inf would spin forever in the float formatting loops. Use pure float semantics instead (NaN: fv != fv; ±inf: fv - fv is NaN, finite gives 0.0).
def _check_special_float(fv: double) -> int:
if fv != fv: return 3
diff: double = fv - fv
@@ -586,7 +580,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
else:
fc = 110
else:
# inf (f_sp==1 +inf 或 f_sp==2 -inf)
# inf (f_sp==1 +inf or f_sp==2 -inf)
if fi_c == 0:
fc = 105
elif fi_c == 1:
@@ -631,7 +625,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
di = 1
else:
scale: double = 1.0
# 上界防护:inf 会让 `work >= scale*10.0` 永真,正常有限值最多 308 次
# Upper-bound guard: inf makes `work >= scale*10.0` always true; finite values need at most 308 iterations
scale_guard: int = 0
while work >= scale * 10.0 and scale_guard < 400:
scale *= 10.0
@@ -745,7 +739,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
else:
ec = 110
else:
# inf (e_sp==1 +inf 或 e_sp==2 -inf)
# inf (e_sp==1 +inf or e_sp==2 -inf)
if ei_c == 0:
ec = 105
elif ei_c == 1:
@@ -785,7 +779,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
ptr += _step
exp10: int = 0
if fv != 0.0:
# 上界防护:inf 会让下面两个循环永真(inf/10==inf、inf*10==inf)
# Upper-bound guard: inf makes the two loops below always true (inf/10==inf, inf*10==inf)
exp_guard: int = 0
while fv >= 10.0 and exp_guard < 400:
fv /= 10.0
@@ -939,7 +933,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
else:
gc = 110
else:
# inf (g_sp==1 +inf 或 g_sp==2 -inf)
# inf (g_sp==1 +inf or g_sp==2 -inf)
if gi_c == 0:
gc = 105
elif gi_c == 1:
@@ -980,7 +974,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
fv_orig: double = fv
exp10: int = 0
if fv != 0.0:
# 上界防护:卡死现场即此循环(inf 会让 fv>=10.0 永真,inf/10==inf)
# Upper-bound guard: this loop is where it hangs (inf makes fv>=10.0 always true, inf/10==inf)
exp_guard: int = 0
while fv >= 10.0 and exp_guard < 400:
fv /= 10.0
@@ -1020,9 +1014,8 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
if prec_e > 0:
temp[1] = 46
g_len = 2
# 小数位逆序存入(个位放 g_len+prec_e-1),temp 直接为正序。
# 原交换反转循环在 TPV 编译产物中失效(小数位未反转,如 456->654),
# 改逆序存储,与 %f 的倒序输出一致,绕开三变量交换。
# Store fraction digits in reverse order (ones place at g_len+prec_e-1) so temp is already forward-ordered.
# The original swap-reverse loop fails in TPV output (digits not reversed, e.g. 456->654); reverse storage matches %f's output and avoids the three-variable swap.
fi: int = 0
fp_inv: uint64 = frac_p
fp_idx: int = g_len + prec_e - 1
@@ -1041,7 +1034,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
g_len = 1
else:
scale: double = 1.0
# 上界防护:inf 会让 `work >= scale*10.0` 永真,正常有限值最多 308 次
# Upper-bound guard: inf makes `work >= scale*10.0` always true; finite values need at most 308 iterations
scale_guard: int = 0
while work >= scale * 10.0 and scale_guard < 400:
scale *= 10.0
@@ -1184,7 +1177,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, ap: str) -> int:
else:
ac = 110
else:
# inf (a_sp==1 +inf 或 a_sp==2 -inf)
# inf (a_sp==1 +inf or a_sp==2 -inf)
if ai_c == 0:
ac = 105
elif ai_c == 1:
+14 -34
View File
@@ -182,10 +182,8 @@ movups [{asminp(out, ASM_DESCR.REG_ANY)}], xmm0""",
def simd_add_array(a: double*, b: double*, out: double*, n: sizet):
"""AVX2 batch: out[i:i+4] = a[i:i+4] + b[i:i+4], i=0,4,8,...
×8 双缓冲(32 doubles/大迭代):a(ymm0-7)+b(ymm8-15) 16 路 load 全部在飞后
做 8 条独立 add→store 链,最大化内存级并行、压榨访存口。
尾巴(不足 8 个 ymm 的块)回退单向量循环,语义与原版一致(floor(n/4) 向量)。
注:ymm0-15 clobber 需 TPV __asm__ 支持(CallGenCBuiltin ASM_MAX_CLOBBERS=32)。"""
×8 double-buffering (32 doubles/iteration): 16 loads in flight (a: ymm0-7, b: ymm8-15), then 8 independent add->store chains; tail falls back to the single-vector loop (floor(n/4) vectors).
Note: ymm0-15 clobber requires TPV __asm__ support (CallGenCBuiltin ASM_MAX_CLOBBERS=32)."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
shr rcx, 2
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
@@ -279,7 +277,7 @@ vzeroupper""",
def simd_mul_array(a: double*, b: double*, out: double*, n: sizet):
"""AVX2 batch: out[i:i+4] = a[i:i+4] * b[i:i+4]
×8 双缓冲(同 simd_add_array)。"""
×8 double-buffering (same as simd_add_array)."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
shr rcx, 2
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
@@ -348,13 +346,8 @@ vzeroupper""",
def simd_fma_array(a: double*, b: double*, c_val: double*, out: double*, n: sizet):
"""AVX2 batch (单趟融合乘加): out[i:i+4] = a[i:i+4]*b[i:i+4] + c_val[i:i+4]
×8 双缓冲(同 simd_mul_array):a(ymm0-7)+b(ymm8-15) 16 路 load 在飞后,
用 8 条 vfmadd213pd 以 c_val 内存操作数为加数一次完成 FMA 并落 out,
避免 mul→tmp→add 的两遍写法(读流量 = 每元素 24B,写 8B,无中间数组)。
语义:vfmadd213pd ymmX, ymmY, mem => ymmX = ymmY*ymmX + mem
ymm8..15 = a(big lane)*b(big lane) + c(big lane),单次舍入。
尾巴(不足 8 个 ymm 的块)回退单向量循环。n 须为 4 的倍数(floor(n/4) 向量)。"""
"""AVX2 batch (single-pass FMA): out[i:i+4] = a[i:i+4]*b[i:i+4] + c_val[i:i+4]
×8 double-buffering: 8 vfmadd213pd using the c_val memory operand, single rounding, no mul->tmp->add pass; tail falls back to the single-vector loop (n must be a multiple of 4)."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
shr rcx, 2
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
@@ -426,15 +419,8 @@ vzeroupper""",
def scalar_fma_array(a: double*, b: double*, c_val: double*, out: double*, n: sizet):
"""标量(逐元素)融合乘加: out[i] = a[i]*b[i] + c_val[i], 一次只算 1 个 double。
与 simd_fma_array 是同一数学内核的"未向量化"对照实现:
* 指令用标量 FMA `vfmadd213sd`(xmm 低位), 与 SIMD 版的 `vfmadd213pd`
同为硬件融合乘加、同为单次舍入 -> 逐元素结果与 SIMD 版**逐位一致**,
CHECK 可直接对照;
* 每次循环 3 读 1 写、8 字节粒度, 相比 ×4 的 ymm 少 4 倍数据并行度,
用来量化"向量化本身"带来的收益(SIMD 加速比)。
n 须 >= 1; n == 0 直接跳过。"""
"""Scalar (element-wise) FMA: out[i] = a[i]*b[i] + c_val[i], one double at a time.
Unvectorized counterpart of simd_fma_array: scalar `vfmadd213sd` vs SIMD `vfmadd213pd`, both hardware FMA with a single rounding, so results are bit-identical. n must be >= 1; n == 0 is skipped."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
mov r9, {asminp(b, ASM_DESCR.REG_ANY)}
@@ -459,10 +445,8 @@ jnz .Lsca_loop
def scalar_add_array(a: double*, b: double*, out: double*, n: sizet):
"""标量(逐元素)加法: out[i] = a[i] + b[i], 一次 1 个 double。
`simd_add_array` 的未向量化对照: 标量 `vaddsd` vs SIMD `vaddpd`,
同为 IEEE 加法 -> 结果逐位一致。"""
"""Scalar (element-wise) add: out[i] = a[i] + b[i], one double at a time.
Unvectorized counterpart of `simd_add_array`: scalar `vaddsd` vs SIMD `vaddpd`, both IEEE add -> bit-identical results."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
mov r9, {asminp(b, ASM_DESCR.REG_ANY)}
@@ -483,10 +467,8 @@ jnz .Lsca_add_loop
def scalar_mul_array(a: double*, b: double*, out: double*, n: sizet):
"""标量(逐元素)乘法: out[i] = a[i] * b[i], 一次 1 个 double。
`simd_mul_array` 的未向量化对照: 标量 `vmulsd` vs SIMD `vmulpd`,
同为 IEEE 乘法 -> 结果逐位一致。"""
"""Scalar (element-wise) multiply: out[i] = a[i] * b[i], one double at a time.
Unvectorized counterpart of `simd_mul_array`: scalar `vmulsd` vs SIMD `vmulpd`, both IEEE multiply -> bit-identical results."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
mov r9, {asminp(b, ASM_DESCR.REG_ANY)}
@@ -507,10 +489,8 @@ jnz .Lsca_mul_loop
def scalar_sqrt_array(a: double*, out: double*, n: sizet):
"""标量(逐元素)平方根: out[i] = sqrt(a[i]), 一次 1 个 double。
`simd_sqrt_array` 的未向量化对照: 标量 `vsqrtsd` vs SIMD `vsqrtpd`,
两者都是 IEEE 正确舍入的平方根 -> 结果逐位一致。"""
"""Scalar (element-wise) square root: out[i] = sqrt(a[i]), one double at a time.
Unvectorized counterpart of `simd_sqrt_array`: scalar `vsqrtsd` vs SIMD `vsqrtpd`, both IEEE correctly-rounded sqrt -> bit-identical results."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
mov r10, {asminp(out, ASM_DESCR.REG_ANY)}
@@ -554,7 +534,7 @@ vzeroupper""",
def simd_sqrt_array(a: double*, out: double*, n: sizet):
"""AVX2 batch: out[i:i+4] = sqrt(a[i:i+4])
×4 展开:4 路独立 vsqrtpd 链隐藏平方根长延迟(吞吐受限场景显著提速)。"""
×4 unrolled: 4 independent vsqrtpd chains hide the long sqrt latency (big speedup when throughput-bound)."""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
shr rcx, 2
mov r8, {asminp(a, ASM_DESCR.REG_ANY)}
+112 -133
View File
@@ -22,20 +22,14 @@ from vqt6._qtnetwork import *
# ============================================================
# 高级 API 类(Pythonic 包装,不透明指针)
# High-level API classes (Pythonic wrappers over opaque pointers)
#
# 为什么每个类都写 __new__:
# Viper 的类实例默认是"创建函数当前帧的 alloca"(见
# CallGenCtor._translate_struct_ctor: tmp = build_alloca(struct_ty))。
# 一旦包装对象被存进字段、或被 return 出函数,帧销毁后该指针即悬垂——
# 表现为读 _ptr 得到垃圾、再把垃圾传给 Qt(如 QLayout::addWidget 崩溃)。
# 编译器在类定义了 __new__ 时改用其返回值作存储指针,故这里统一
# stdlib.malloc 堆分配包装壳(单字段 { _ptr } = 8 字节,进程期存活)。
# 同模块内即用即弃的场景不受影响;跨帧/跨模块存取则必须靠它。
# Every wrapper defines __new__ to heap-allocate its shell via stdlib.malloc
# ({ _ptr }, 8 bytes) so _ptr stays valid across frames and modules.
# ============================================================
# ============================================================
# 模块级 helper:QWidget 基础操作(各控件类复用)
# Module-level helpers: base QWidget operations (shared by widget classes)
# ============================================================
def _w_show(ptr: t.CPtr):
vqt6_qwidget_show(ptr)
@@ -86,7 +80,7 @@ def _w_set_tool_tip(ptr: t.CPtr, tip: str):
def _w_text_of(qstr: QtStringPtr) -> str:
"""QtStringPtr → Viper str(复制到 malloc 缓冲)"""
"""QtStringPtr -> Viper str (copied into a malloc buffer)."""
buf: bytes = stdlib.malloc(1024)
vqt6_qstring_to_utf8(qstr, buf, 1024)
s: str = buf
@@ -95,11 +89,11 @@ def _w_text_of(qstr: QtStringPtr) -> str:
# ============================================================
# 树状控件统计助手(返回 int,规避跨模块 FFI 返回指针)
# 叶子勾选态: 0=未选 1=半选 2=选中
# Tree widget statistics helpers (return int to avoid cross-module FFI ptr returns)
# Leaf check state: 0=unchecked 1=partial 2=checked
# ============================================================
def qtree_checked_count(tw: QtTreeWidgetPtr) -> int:
"""统计被勾选的叶子节点数"""
"""Count checked leaf nodes."""
n: int = 0
gi: int = 0
top_n: int = vqt6_qtreewidget_top_level_item_count(tw)
@@ -117,7 +111,7 @@ def qtree_checked_count(tw: QtTreeWidgetPtr) -> int:
def qtree_checked_size(tw: QtTreeWidgetPtr) -> int:
"""统计被勾选叶子节点的磁盘占用合计(MB)"""
"""Sum disk usage (MB) of checked leaf nodes."""
mb: int = 0
gi: int = 0
top_n: int = vqt6_qtreewidget_top_level_item_count(tw)
@@ -135,7 +129,7 @@ def qtree_checked_size(tw: QtTreeWidgetPtr) -> int:
def qtree_click_toggle(tw: QtTreeWidgetPtr):
"""点击带子节点的当前行 → 展开/折叠"""
"""Toggle expansion of the current row when it has children."""
cur: QtTreeWidgetItemPtr = vqt6_qtreewidget_current_item(tw)
if cur is None:
return
@@ -145,17 +139,16 @@ def qtree_click_toggle(tw: QtTreeWidgetPtr):
novirtual class QApplication:
"""QApplication 包装(不透明指针)"""
"""QApplication wrapper (opaque pointer)."""
_ptr: QtAppPtr
def __new__(self, argv: list[str]*) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, argv: list[str]*):
# 把 argv 转换为 argc/argv 数组
# Convert argv into an argc/argv array.
argc: int = 1
# Viper 端 argv 是 c_char_p 指针数组
# 这里仅传空 argv,避免 C ABI 复杂性
# Viper argv is a c_char_p pointer array; an empty argv avoids C ABI complexity.
argv_ptr: t.CPtr = t.CPtr(0)
self._ptr = vqt6_qapplication_create(argc, argv_ptr)
if self._ptr is None:
@@ -180,24 +173,20 @@ novirtual class QApplication:
return vqt6_qapplication_desktop_height(self._ptr)
def set_font_family(self, family: str):
"""设置应用级字体族(全局生效)
"""Set the application-wide font family (global).
必须在创建任何控件之前调用:Qt 是在控件构造时从应用字体继承的,
之后再设只会影响"此后新建"的控件。
这里必须走应用字体而不是给主窗口挂样式表(font-family):Qt Charts 的
标题/图例/坐标轴标签画在 QChart(QGraphicsWidget)上,样式表够不到它们,
只有应用字体能一并覆盖。
Must be called before creating any widget; it also reaches Qt Charts text
(titles/legend/axis labels) that stylesheets cannot.
"""
vqt6_qapplication_set_font_family_utf8(self._ptr, family)
novirtual class QWidget:
"""QWidget 包装"""
"""QWidget wrapper."""
_ptr: QtWidgetPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -257,11 +246,11 @@ novirtual class QWidget:
novirtual class QLabel:
"""QLabel 包装"""
"""QLabel wrapper."""
_ptr: QtLabelPtr
def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, text: str = "", parent: "QWidget | None" = None):
text_qs: QtStringPtr = vqt6_qstring_create(text)
@@ -296,11 +285,11 @@ novirtual class QLabel:
novirtual class QPushButton:
"""QPushButton 包装"""
"""QPushButton wrapper."""
_ptr: QtPushButtonPtr
def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, text: str = "", parent: "QWidget | None" = None):
text_qs: QtStringPtr = vqt6_qstring_create(text)
@@ -348,7 +337,7 @@ novirtual class QPushButton:
def setFlat(self, flat: int):
vqt6_qpushbutton_set_flat(self._ptr, flat)
# 回调(cb 传 Viper 模块级函数,签名 def cb(slot_id: int, data: t.CPtr))
# Callbacks: cb is a Viper module-level function cb(slot_id: int, data: t.CPtr).
def clicked(self, cb: bytes, data: bytes = 0):
vqt6_qpushbutton_connect_clicked(self._ptr, cb, data)
@@ -357,19 +346,13 @@ novirtual class QPushButton:
# ============================================================
# 菜单:QAction / QMenu / QMenuBar / QStatusBar
# Menus: QAction / QMenu / QMenuBar / QStatusBar
#
# 按依赖顺序定义(QAction → QMenu → QMenuBar → QStatusBar), 并整体排在
# QMainWindow 之前, 使 addMenu()/addActionWithText() 的返回注解不再前向引用。
#
# 注意: 返回"包装类对象"(按值 struct)的方法无法跨模块解析返回类型——
# 依赖模块 text.ll 里是 %"sha1.QStatusBar" 这类 struct 值(不含 *,
# _ParseLLVMRetTyStr 解析失败), 而 vpi 缓存的是带引号的前向注解
# ('QStatusBar'), 同样解析失败 → 调用方 fatal(mro_ret)。
# 故 QMainWindow.menuBar/statusBar 一律返回裸指针。
# Defined before QMainWindow so return annotations need no forward reference;
# menuBar/statusBar return raw pointers since wrapper-value returns don't cross modules.
# ============================================================
novirtual class QAction:
"""QAction 包装"""
"""QAction wrapper."""
_ptr: QtActionPtr
def __init__(self, text: str = ""):
@@ -421,7 +404,7 @@ novirtual class QAction:
novirtual class QMenu:
"""QMenu 包装"""
"""QMenu wrapper."""
_ptr: QtMenuPtr
def __init__(self, title: str = ""):
@@ -466,7 +449,7 @@ novirtual class QMenu:
novirtual class QMenuBar:
"""QMenuBar 包装"""
"""QMenuBar wrapper."""
_ptr: QtMenuBarPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -493,7 +476,7 @@ novirtual class QMenuBar:
novirtual class QStatusBar:
"""QStatusBar 包装"""
"""QStatusBar wrapper."""
_ptr: QtStatusBarPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -520,14 +503,14 @@ novirtual class QStatusBar:
# ============================================================
# 窗口:QMainWindow(依赖上面的 QMenuBar / QStatusBar, 故排在其后)
# Window: QMainWindow (defined after the QMenuBar / QStatusBar it depends on).
# ============================================================
novirtual class QMainWindow:
"""QMainWindow 包装"""
"""QMainWindow wrapper."""
_ptr: QtMainWindowPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -541,14 +524,14 @@ novirtual class QMainWindow:
vqt6_qmainwindow_set_central_widget(self._ptr, w)
def menuBar(self) -> QtMenuBarPtr:
"""返回窗口自带的菜单栏裸指针(不新建; 需要包装类请自行 new QMenuBar 后挂 _ptr)"""
"""Return the window's own menu bar as a raw pointer (no wrapper created)."""
return vqt6_qmainwindow_menu_bar(self._ptr)
def setMenuBar(self, mb: t.CPtr):
vqt6_qmainwindow_set_menu_bar(self._ptr, mb)
def statusBar(self) -> QtStatusBarPtr:
"""返回窗口自带的状态栏裸指针(不新建; 需要包装类请自行 new QStatusBar 后挂 _ptr)"""
"""Return the window's own status bar as a raw pointer (no wrapper created)."""
return vqt6_qmainwindow_status_bar(self._ptr)
def addToolBar(self, tb: t.CPtr):
@@ -586,7 +569,7 @@ novirtual class QMainWindow:
novirtual class QDialog:
"""QDialog 包装"""
"""QDialog wrapper."""
_ptr: QtDialogPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -638,14 +621,14 @@ novirtual class QDialog:
# ============================================================
# 输入控件:QLineEdit / QCheckBox / QRadioButton
# Input widgets: QLineEdit / QCheckBox / QRadioButton
# ============================================================
novirtual class QLineEdit:
"""QLineEdit 包装"""
"""QLineEdit wrapper."""
_ptr: QtLineEditPtr
def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, text: str = "", parent: "QWidget | None" = None):
qs: QtStringPtr = vqt6_qstring_create(text)
@@ -708,11 +691,11 @@ novirtual class QLineEdit:
novirtual class QCheckBox:
"""QCheckBox 包装"""
"""QCheckBox wrapper."""
_ptr: QtCheckBoxPtr
def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, text: str = "", parent: "QWidget | None" = None):
qs: QtStringPtr = vqt6_qstring_create(text)
@@ -770,11 +753,11 @@ novirtual class QCheckBox:
novirtual class QRadioButton:
"""QRadioButton 包装"""
"""QRadioButton wrapper."""
_ptr: QtRadioButtonPtr
def __new__(self, text: str = "", parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, text: str = "", parent: "QWidget | None" = None):
qs: QtStringPtr = vqt6_qstring_create(text)
@@ -823,10 +806,10 @@ novirtual class QRadioButton:
# ============================================================
# 选择控件:QComboBox / QSlider / QSpinBox / QProgressBar
# Selection widgets: QComboBox / QSlider / QSpinBox / QProgressBar
# ============================================================
novirtual class QComboBox:
"""QComboBox 包装"""
"""QComboBox wrapper."""
_ptr: QtComboBoxPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -897,7 +880,7 @@ novirtual class QComboBox:
novirtual class QSlider:
"""QSlider 包装"""
"""QSlider wrapper."""
_ptr: QtSliderPtr
def __init__(self, orientation: int = QT_HORIZONTAL, parent: "QWidget | None" = None):
@@ -952,11 +935,11 @@ novirtual class QSlider:
novirtual class QSpinBox:
"""QSpinBox 包装"""
"""QSpinBox wrapper."""
_ptr: QtSpinBoxPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -1020,11 +1003,11 @@ novirtual class QSpinBox:
novirtual class QProgressBar:
"""QProgressBar 包装"""
"""QProgressBar wrapper."""
_ptr: QtProgressBarPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -1083,10 +1066,10 @@ novirtual class QProgressBar:
# ============================================================
# 文本:QTextEdit / QPlainTextEdit
# Text: QTextEdit / QPlainTextEdit
# ============================================================
novirtual class QTextEdit:
"""QTextEdit 包装"""
"""QTextEdit wrapper."""
_ptr: QtTextEditPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -1144,11 +1127,11 @@ novirtual class QTextEdit:
novirtual class QPlainTextEdit:
"""QPlainTextEdit 包装"""
"""QPlainTextEdit wrapper."""
_ptr: QtPlainTextEditPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -1200,10 +1183,10 @@ novirtual class QPlainTextEdit:
# ============================================================
# 列表:QListWidget / QTabWidget / QStackedWidget
# Lists: QListWidget / QTabWidget / QStackedWidget
# ============================================================
novirtual class QListWidget:
"""QListWidget 包装"""
"""QListWidget wrapper."""
_ptr: QtListWidgetPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -1249,13 +1232,10 @@ novirtual class QListWidget:
def addBubble(self, text: str, qss: str, right_align: int = 0,
max_bubble_w: int = 420, hpad: int = 28, vpad: int = 16):
"""追加一条聊天气泡(微信/QQ 风格,右=自己/左=对方)
"""Append a chat bubble (WeChat/QQ style, right=self / left=peer).
qss: 气泡 QLabel 样式(如 background-color/border-radius/padding)
right_align: 1=右对齐(自己),0=左对齐(对方)
max_bubble_w: 气泡最大宽度(超出自动换行)
hpad: QSS padding 左右合计(用于气泡宽度估算)
vpad: QSS padding 上下合计(用于气泡高度计算,防底部裁切)
qss: bubble QLabel style; right_align: 1=right(self)/0=left; max_bubble_w:
max width; hpad/vpad: horizontal/vertical QSS padding totals for sizing.
"""
tqs: QtStringPtr = vqt6_qstring_create(text)
qqs: QtStringPtr = vqt6_qstring_create(qss)
@@ -1265,7 +1245,7 @@ novirtual class QListWidget:
def replaceLastBubble(self, text: str, qss: str, right_align: int = 0,
max_bubble_w: int = 420, hpad: int = 28, vpad: int = 16):
"""删除最后一条气泡并按新文本重建(流式回复逐字刷新用,参数同 addBubble)"""
"""Remove the last bubble and rebuild it from new text (for streaming; params as addBubble)."""
tqs: QtStringPtr = vqt6_qstring_create(text)
qqs: QtStringPtr = vqt6_qstring_create(qss)
vqt6_qlistwidget_replace_last_bubble(self._ptr, tqs, qqs, right_align,
@@ -1299,7 +1279,7 @@ novirtual class QListWidget:
novirtual class QTreeWidgetItem:
"""QTreeWidgetItem 包装(树节点,勾选态 0=未选 2=选中)"""
"""QTreeWidgetItem wrapper (tree node; check state 0=unchecked 2=checked)."""
_ptr: QtTreeWidgetItemPtr
def __init__(self, text: str = ""):
@@ -1341,7 +1321,7 @@ novirtual class QTreeWidgetItem:
return vqt6_qtreewidgetitem_check_state(self._ptr)
def setSize(self, mb: int):
"""把磁盘占用(MB)存到节点 UserRole(统计用)"""
"""Store disk usage (MB) in the node's UserRole (for statistics)."""
vqt6_qtreewidgetitem_set_data_int(self._ptr, mb)
def setExpanded(self, expanded: int):
@@ -1352,7 +1332,7 @@ novirtual class QTreeWidgetItem:
novirtual class QTreeWidget:
"""QTreeWidget 包装"""
"""QTreeWidget wrapper."""
_ptr: QtTreeWidgetPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -1423,11 +1403,11 @@ novirtual class QTreeWidget:
novirtual class QTabWidget:
"""QTabWidget 包装"""
"""QTabWidget wrapper."""
_ptr: QtTabWidgetPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -1486,7 +1466,7 @@ novirtual class QTabWidget:
novirtual class QStackedWidget:
"""QStackedWidget 包装"""
"""QStackedWidget wrapper."""
_ptr: QtStackedWidgetPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -1535,10 +1515,10 @@ novirtual class QStackedWidget:
# ============================================================
# 布局:QHBoxLayout / QVBoxLayout / QGridLayout
# Layouts: QHBoxLayout / QVBoxLayout / QGridLayout
# ============================================================
novirtual class QHBoxLayout:
"""QHBoxLayout 包装(addWidget 传控件 ._ptr)"""
"""QHBoxLayout wrapper (pass the widget's ._ptr to addWidget)."""
_ptr: QtBoxLayoutPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -1572,7 +1552,7 @@ novirtual class QHBoxLayout:
novirtual class QVBoxLayout:
"""QVBoxLayout 包装(addWidget 传控件 ._ptr)"""
"""QVBoxLayout wrapper (pass the widget's ._ptr to addWidget)."""
_ptr: QtBoxLayoutPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -1606,7 +1586,7 @@ novirtual class QVBoxLayout:
novirtual class QGridLayout:
"""QGridLayout 包装(addWidget 传控件 ._ptr)"""
"""QGridLayout wrapper (pass the widget's ._ptr to addWidget)."""
_ptr: QtGridLayoutPtr
def __init__(self, parent: "QWidget | None" = None):
@@ -1631,10 +1611,10 @@ novirtual class QGridLayout:
# ============================================================
# 对话框:QMessageBox / QFileDialog(静态方法)
# Dialogs: QMessageBox / QFileDialog (static methods)
# ============================================================
novirtual class QMessageBox:
"""QMessageBox 静态对话框"""
"""QMessageBox static dialog wrapper."""
_ptr: QtMessageBoxPtr
@staticmethod
@@ -1698,7 +1678,7 @@ novirtual class QMessageBox:
novirtual class QFileDialog:
"""QFileDialog 静态对话框"""
"""QFileDialog static dialog wrapper."""
_ptr: QtFileDialogPtr
@staticmethod
@@ -1743,14 +1723,14 @@ novirtual class QFileDialog:
# ============================================================
# 定时器:QTimer
# Timer: QTimer
# ============================================================
novirtual class QTimer:
"""QTimer 包装"""
"""QTimer wrapper."""
_ptr: QtTimerPtr
def __new__(self) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self):
self._ptr = vqt6_qtimer_create()
@@ -1780,10 +1760,10 @@ novirtual class QTimer:
# ============================================================
# 绘图基础:QColor / QFont
# Drawing basics: QColor / QFont
# ============================================================
novirtual class QColor:
"""QColor 包装"""
"""QColor wrapper."""
_ptr: QtColorPtr
def __init__(self, r: int, g: int, b: int, a: int = 255):
@@ -1805,7 +1785,7 @@ novirtual class QColor:
novirtual class QFont:
"""QFont 包装"""
"""QFont wrapper."""
_ptr: QtFontPtr
def __init__(self, family: str = "", point_size: int = 0):
@@ -1838,15 +1818,15 @@ novirtual class QFont:
# ============================================================
# 剪贴板(复制链接等)
# Clipboard (copying links, etc.)
# ============================================================
def clipboard_set_text(text: str):
"""把文本写入系统剪贴板"""
"""Write text to the system clipboard."""
vqt6_clipboard_set_text_utf8(text)
def clipboard_text() -> str:
"""读取系统剪贴板文本"""
"""Read text from the system clipboard."""
buf: bytes = stdlib.malloc(4096)
vqt6_clipboard_text_utf8(buf, 4096)
s: str = buf
@@ -1855,18 +1835,18 @@ def clipboard_text() -> str:
# ============================================================
# 自绘控件:Canvas / Graph3D / ChartView
# Custom-painted widgets: Canvas / Graph3D / ChartView
# ============================================================
novirtual class Canvas:
"""手绘板 / 28x28 灰度显示控件
"""Freehand drawing board / 28x28 grayscale display widget.
手绘模式下鼠标左键描绘; toMnist28 把笔迹按 MNIST 预处理
(包围盒缩放到 20x20 后质心居中)输出为 28x28 灰度字节数组。
Left-drag draws; toMnist28 preprocesses the ink (bounding box scaled to
20x20, centroid-centered) into a 28x28 grayscale byte array.
"""
_ptr: QtCanvasPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -1883,7 +1863,7 @@ novirtual class Canvas:
return vqt6_canvas_has_ink(self._ptr)
def setMode(self, mode: int):
"""0 = 手绘, 1 = 图像显示"""
"""0 = freehand drawing, 1 = image display."""
vqt6_canvas_set_mode(self._ptr, mode)
def mode(self) -> int:
@@ -1896,22 +1876,22 @@ novirtual class Canvas:
vqt6_canvas_set_grid(self._ptr, on)
def setScanLine(self, y_norm: int):
"""0..1000 扫描线位置, <0 关闭"""
"""Scan line position 0..1000; <0 disables it."""
vqt6_canvas_set_scan_line(self._ptr, y_norm)
def setBackground(self, r: int, g: int, b: int):
vqt6_canvas_set_bg(self._ptr, r, g, b)
def toMnist28(self, out: bytes) -> int:
"""笔迹 → 28x28 灰度(写入 out); 无墨迹返回 0"""
"""Ink -> 28x28 grayscale (written to out); returns 0 if there is no ink."""
return vqt6_canvas_to_mnist28(self._ptr, out)
def setImage28(self, inp: bytes):
"""28x28 灰度 → 显示(自动切到图像显示模式)"""
"""Show a 28x28 grayscale image (switches to image display mode)."""
vqt6_canvas_set_image28(self._ptr, inp)
def changed(self, cb: bytes, data: bytes = 0):
"""笔迹完成(鼠标抬起)回调: def cb(slot_id: int, data: t.CPtr)"""
"""Ink-finished (mouse release) callback: cb(slot_id: int, data: t.CPtr)."""
vqt6_canvas_connect_changed(self._ptr, cb, data)
def show(self):
@@ -1937,16 +1917,15 @@ novirtual class Canvas:
novirtual class Graph3D:
"""软件 3D 神经网络结构图控件
"""Software 3D neural-network structure view.
层次沿 Z 轴排布(输入层在最远端), 各层按近正方形网格铺开;
相邻层前向全连接。左键拖拽旋转, 滚轮缩放。
渲染 API 与后端无关, 将来换 OpenGL 实现时调用代码无需改动。
Layers run along Z (input farthest); each layer is a near-square grid and
adjacent layers are fully connected. Drag to rotate, wheel to zoom.
"""
_ptr: QtGraph3DPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -1957,7 +1936,7 @@ novirtual class Graph3D:
raise RuntimeError("Failed to create Graph3D")
def setup(self, layer_sizes: int*, n_layers: int):
"""layer_sizes: int 数组指针(输入层→输出层各层节点数)"""
"""layer_sizes: int array pointer (node count per layer, input -> output)."""
vqt6_graph3d_setup(self._ptr, layer_sizes, n_layers)
def clear(self):
@@ -1970,18 +1949,18 @@ novirtual class Graph3D:
return vqt6_graph3d_edge_count(self._ptr)
def setLayerAlpha(self, layer: int, alpha: double):
"""逐层点亮: 0..1"""
"""Per-layer brightness: 0..1."""
vqt6_graph3d_set_layer_alpha(self._ptr, layer, alpha)
def setAllLayerAlpha(self, alpha: double):
vqt6_graph3d_set_all_layer_alpha(self._ptr, alpha)
def setValues(self, values: double*, n: int):
"""每节点激活值(0..1), 影响亮度与半径"""
"""Per-node activation (0..1), affecting brightness and radius."""
vqt6_graph3d_set_values(self._ptr, values, n)
def setEdgeWeights(self, w: double*, n: int):
"""边权重(按层间顺序), 影响颜色/透明度并按 |w| 裁剪"""
"""Edge weights (in inter-layer order); affect color/alpha and are culled by |w|."""
vqt6_graph3d_set_edge_weights(self._ptr, w, n)
def setEdgeThreshold(self, tval: double):
@@ -2029,9 +2008,9 @@ novirtual class Graph3D:
novirtual class ChartView:
"""Qt Charts 折线图控件(右侧 Y 轴可选)
"""Qt Charts line-chart widget (optional right Y axis).
典型用法:
Typical usage:
cv = ChartView()
loss = cv.addLine("loss", 255, 107, 107, 0)
acc = cv.addLine("acc", 77, 171, 247, 1)
@@ -2041,7 +2020,7 @@ novirtual class ChartView:
_ptr: QtChartViewPtr
def __new__(self, parent: "QWidget | None" = None) -> t.CPtr:
return stdlib.malloc(8) # 包装壳堆分配, 见本文件顶部说明
return stdlib.malloc(8) # heap-allocated shell, see note at top of file
def __init__(self, parent: "QWidget | None" = None):
if parent is not None and parent._ptr is not None:
@@ -2076,15 +2055,15 @@ novirtual class ChartView:
vqt6_chartview_set_background(self._ptr, r, g, b)
def setMaxPoints(self, max_points: int):
"""滑动窗口: 每条折线超过 max_points 就丢弃最旧的点(<=0 表示不限制)"""
"""Sliding window: drop a series' oldest points past max_points (<=0 = no limit)."""
vqt6_chartview_set_max_points(self._ptr, max_points)
def openZoom(self, title: str):
"""打开(或复用)大窗查看当前曲线; 关闭只是隐藏, 不销毁"""
"""Open (or reuse) a large window for the current curves; closing only hides it."""
vqt6_chartview_open_zoom(self._ptr, title)
def syncZoom(self):
"""把当前曲线与轴范围同步到大窗(大窗未打开时什么也不做)"""
"""Sync current curves and axis ranges to the large window (no-op if not open)."""
vqt6_chartview_sync_zoom(self._ptr)
def show(self):
@@ -2107,10 +2086,10 @@ novirtual class ChartView:
# ============================================================
# 模块导出
# Module exports
# ============================================================
__all__ = [
# 高层类
# High-level classes
"QApplication", "QWidget", "QLabel", "QPushButton",
"QMainWindow", "QDialog",
"QLineEdit", "QCheckBox", "QRadioButton",
@@ -2122,10 +2101,10 @@ __all__ = [
"QMessageBox", "QFileDialog", "QTimer",
"QMenuBar", "QMenu", "QAction", "QStatusBar",
"QColor", "QFont",
# 自绘控件
# Custom-painted widgets
"Canvas", "Graph3D", "ChartView",
"clipboard_set_text", "clipboard_text",
# 类型
# Types
"QtObjectPtr", "QtStringPtr", "QtByteArrayPtr", "QtWidgetPtr", "QtAppPtr",
"QtLabelPtr", "QtPushButtonPtr",
"QtTreeWidgetPtr", "QtTreeWidgetItemPtr",
@@ -2133,7 +2112,7 @@ __all__ = [
"QtMenuBarPtr", "QtMenuPtr", "QtActionPtr", "QtStatusBarPtr",
"QtCanvasPtr", "QtGraph3DPtr", "QtChartViewPtr", "QtChartSeriesPtr",
"QtTcpSocketPtr", "QtHostAddressPtr", "QtNetworkRequestPtr", "QtNetworkReplyPtr",
# 常量
# Constants
"QT_KEY_ESCAPE", "QT_KEY_RETURN", "QT_KEY_A", "QT_KEY_Z",
"QT_LEFT_BUTTON", "QT_RIGHT_BUTTON", "QT_MID_BUTTON",
"QT_HORIZONTAL", "QT_VERTICAL",
+27 -28
View File
@@ -10,34 +10,34 @@ from vqt6._types import *
# ============================================================
# ============================================================
# QString — UTF-16 字符串
# QString — UTF-16 string
# ============================================================
# 创建 QString(从 UTF-8 C 字符串)
# Create a QString from a UTF-8 C string.
def vqt6_qstring_create(utf8: char*) -> QtStringPtr | t.State: pass
# 获取 QString 的 UTF-8 表示(写入 buf,返回实际长度,不含 \0)
# buf 需足够大(建议 4 * qstring.length() + 1)
# Get the UTF-8 form of a QString (writes to buf, returns the actual length, excluding \0).
# buf must be large enough (suggested 4 * qstring.length() + 1).
def vqt6_qstring_to_utf8(qstr: QtStringPtr, buf: char*, buf_size: sizet) -> sizet | t.State: pass
# 释放 QString
# Free a QString.
def vqt6_qstring_free(qstr: QtStringPtr) -> t.State: pass
# 获取 QString 字符数
# Get the QString character count.
def vqt6_qstring_length(qstr: QtStringPtr) -> int | t.State: pass
# QString 是否为空
# Whether the QString is empty.
def vqt6_qstring_is_empty(qstr: QtStringPtr) -> int | t.State: pass
# QString 比较
# Compare two QStrings.
def vqt6_qstring_equals(qstr: QtStringPtr, other: QtStringPtr) -> int | t.State: pass
# QString 拼接(返回新 QString)
# Concatenate QStrings (returns a new QString).
def vqt6_qstring_append(qstr: QtStringPtr, other: QtStringPtr) -> QtStringPtr | t.State: pass
def vqt6_asset_url_utf8(buf: char*, buf_size: sizet, name: char*) -> t.State: pass
# ============================================================
# QByteArray — 字节数组
# QByteArray — byte array
# ============================================================
def vqt6_qbytearray_create(data: t.CPtr, size: sizet) -> QtByteArrayPtr | t.State: pass
def vqt6_qbytearray_data(qba: QtByteArrayPtr, out_size: sizet*) -> t.CPtr | t.State: pass
@@ -46,9 +46,9 @@ def vqt6_qbytearray_free(qba: QtByteArrayPtr) -> t.State: pass
# ============================================================
# QObject — 所有 Qt 对象的基类
# QObject — base class of all Qt objects
# ============================================================
# 注意: Qt 对象的内存由 C++ 管理,Viper 端不应直接 free
# Note: Qt object memory is managed by C++; do not free it directly from Viper.
def vqt6_qobject_delete(obj: QtObjectPtr) -> t.State: pass
def vqt6_qobject_delete_later(obj: QtObjectPtr) -> t.State: pass
def vqt6_qobject_set_object_name(obj: QtObjectPtr, name: QtStringPtr) -> t.State: pass
@@ -56,23 +56,22 @@ def vqt6_qobject_object_name(obj: QtObjectPtr) -> QtStringPtr | t.State: pass
def vqt6_qobject_set_parent(child: QtObjectPtr, parent: QtObjectPtr) -> t.State: pass
def vqt6_qobject_parent(obj: QtObjectPtr) -> QtObjectPtr | t.State: pass
# 信号/槽连接 (简化版: 函数指针形式)
# Signal/slot connection (simplified).
# typedef void (*vqt6_callback_t)(void* user_data);
# vqt6_qobject_connect_typed(sender, signal_id, receiver, slot_id, type) 由 C++ 桥接层处理
# 由于函数指针在 C ABI 中复杂,我们采用字符串 signal/slot 名 + 全局回调注册表
# Uses string signal/slot names + a global callback registry (function pointers are complex in the C ABI).
def vqt6_qobject_connect(sender: QtObjectPtr, signal_name: char*,
receiver: QtObjectPtr, slot_name: char*) -> int | t.State: pass
def vqt6_qobject_disconnect(sender: QtObjectPtr, signal_name: char*,
receiver: QtObjectPtr, slot_name: char*) -> int | t.State: pass
# 事件过滤器
# Event filter
def vqt6_qobject_install_event_filter(watched: QtObjectPtr, filter: QtObjectPtr) -> t.State: pass
def vqt6_qobject_remove_event_filter(watched: QtObjectPtr, filter: QtObjectPtr) -> t.State: pass
# ============================================================
# QCoreApplication — 事件循环基类
# QCoreApplication — base event loop class
# ============================================================
def vqt6_qcoreapp_create(app_name: char*) -> QtCoreAppPtr | t.State: pass
def vqt6_qcoreapp_instance() -> QtCoreAppPtr | t.State: pass
@@ -89,7 +88,7 @@ def vqt6_qcoreapp_set_application_name(app: QtCoreAppPtr, name: QtStringPtr) ->
# ============================================================
# QTimer — 定时器
# QTimer — timer
# ============================================================
def vqt6_qtimer_create() -> QtTimerPtr | t.State: pass
def vqt6_qtimer_create_with_parent(parent: QtObjectPtr) -> QtTimerPtr | t.State: pass
@@ -100,12 +99,12 @@ def vqt6_qtimer_interval(timer: QtTimerPtr) -> int | t.State: pass
def vqt6_qtimer_is_active(timer: QtTimerPtr) -> int | t.State: pass
def vqt6_qtimer_set_single_shot(timer: QtTimerPtr, single_shot: int) -> t.State: pass
def vqt6_qtimer_is_single_shot(timer: QtTimerPtr) -> int | t.State: pass
# timeout 信号 → 回调(cb 是 Viper 模块级函数,签名 (slot_id: int, user_data: t.CPtr))
# timeout signal -> callback (cb is a Viper module-level function (slot_id: int, user_data: t.CPtr)).
def vqt6_qtimer_connect_timeout(timer: QtTimerPtr, cb: bytes, user_data: bytes) -> t.State: pass
# ============================================================
# QVariant — 通用数据容器
# QVariant — generic data container
# ============================================================
def vqt6_qvariant_create_int(value: int) -> QtVariantPtr | t.State: pass
def vqt6_qvariant_create_double(value: double) -> QtVariantPtr | t.State: pass
@@ -121,7 +120,7 @@ def vqt6_qvariant_free(v: QtVariantPtr) -> t.State: pass
# ============================================================
# QEvent — 事件基类
# QEvent — base event class
# ============================================================
def vqt6_qevent_type(event: QtEventPtr) -> int | t.State: pass
def vqt6_qevent_accept(event: QtEventPtr) -> t.State: pass
@@ -130,7 +129,7 @@ def vqt6_qevent_is_accepted(event: QtEventPtr) -> int | t.State: pass
# ============================================================
# 字符串列表 (QStringList / QList<QString>)
# String list (QStringList / QList<QString>)
# ============================================================
def vqt6_qstringlist_create() -> QtStringListPtr | t.State: pass
def vqt6_qstringlist_append(list: QtStringListPtr, str: QtStringPtr) -> t.State: pass
@@ -140,7 +139,7 @@ def vqt6_qstringlist_free(list: QtStringListPtr) -> t.State: pass
# ============================================================
# 字节数组列表 (QList<QByteArray>)
# Byte-array list (QList<QByteArray>)
# ============================================================
def vqt6_qbytearraylist_create() -> QtByteArrayListPtr | t.State: pass
def vqt6_qbytearraylist_append(list: QtByteArrayListPtr, qba: QtByteArrayPtr) -> t.State: pass
@@ -150,18 +149,18 @@ def vqt6_qbytearraylist_free(list: QtByteArrayListPtr) -> t.State: pass
# ============================================================
# 全局回调注册表
# Global callback registry
# ============================================================
# 用于从 C++ 端回调 Viper 代码
# Viper 注册一个 slot 函数(用整数 ID 标识),C++ 在触发信号时通过 ID 查找
# Lets C++ call back into Viper: Viper registers a slot function (integer ID) that
# C++ looks up by ID when a signal fires.
# typedef void (*vqt6_slot_callback_t)(int slot_id, void* user_data);
def vqt6_register_slot(slot_id: int, user_data: t.CPtr) -> t.State: pass
def vqt6_unregister_slot(slot_id: int) -> t.State: pass
def vqt6_trigger_slot(slot_id: int) -> t.State: pass # 测试用
def vqt6_trigger_slot(slot_id: int) -> t.State: pass # for testing
# ============================================================
# 剪贴板 — 复制链接等
# Clipboard — copying links, etc.
# ============================================================
def vqt6_clipboard_set_text_utf8(text: char*) -> t.State: pass
def vqt6_clipboard_text_utf8(buf: char*, buf_size: int) -> int | t.State: pass
+14 -14
View File
@@ -7,7 +7,7 @@ from vqt6._types import *
# ============================================================
# ============================================================
# QColor — 颜色
# QColor — color
# ============================================================
extern def vqt6_qcolor_create_rgb(r: int, g: int, b: int) -> QtColorPtr
extern def vqt6_qcolor_create_rgba(r: int, g: int, b: int, a: int) -> QtColorPtr
@@ -29,7 +29,7 @@ extern def vqt6_qcolor_free(c: QtColorPtr)
# ============================================================
# QFont — 字体
# QFont — font
# ============================================================
extern def vqt6_qfont_create() -> QtFontPtr
extern def vqt6_qfont_create_with_family(family: QtStringPtr) -> QtFontPtr
@@ -54,7 +54,7 @@ extern def vqt6_qfont_free(font: QtFontPtr)
# ============================================================
# QFontMetrics — 字体度量
# QFontMetrics — font metrics
# ============================================================
extern def vqt6_qfontmetrics_create(font: QtFontPtr) -> QtFontMetricsPtr
extern def vqt6_qfontmetrics_width(fm: QtFontMetricsPtr, text: QtStringPtr) -> int
@@ -65,7 +65,7 @@ extern def vqt6_qfontmetrics_free(fm: QtFontMetricsPtr)
# ============================================================
# QPixmap — 像素图
# QPixmap — pixmap
# ============================================================
extern def vqt6_qpixmap_create() -> QtPixmapPtr
extern def vqt6_qpixmap_create_with_size(w: int, h: int) -> QtPixmapPtr
@@ -81,7 +81,7 @@ extern def vqt6_qpixmap_free(pm: QtPixmapPtr)
# ============================================================
# QImage — 图像
# QImage — image
# ============================================================
extern def vqt6_qimage_create() -> QtImagePtr
extern def vqt6_qimage_create_with_size(w: int, h: int, format: int) -> QtImagePtr
@@ -97,7 +97,7 @@ extern def vqt6_qimage_free(img: QtImagePtr)
# ============================================================
# QPainter — 绘画器
# QPainter — painter
# ============================================================
extern def vqt6_qpainter_create() -> QtPainterPtr
extern def vqt6_qpainter_begin(p: QtPainterPtr, device: QtPixmapPtr) -> int
@@ -112,7 +112,7 @@ extern def vqt6_qpainter_font(p: QtPainterPtr) -> QtFontPtr
extern def vqt6_qpainter_set_opacity(p: QtPainterPtr, opacity: double)
extern def vqt6_qpainter_opacity(p: QtPainterPtr) -> double
# 绘图原语
# Drawing primitives
extern def vqt6_qpainter_draw_line(p: QtPainterPtr, x1: int, y1: int, x2: int, y2: int)
extern def vqt6_qpainter_draw_rect(p: QtPainterPtr, x: int, y: int, w: int, h: int)
extern def vqt6_qpainter_fill_rect(p: QtPainterPtr, x: int, y: int, w: int, h: int, color: QtColorPtr)
@@ -130,7 +130,7 @@ extern def vqt6_qpainter_draw_pixmap(p: QtPainterPtr, x: int, y: int, pixmap: Qt
extern def vqt6_qpainter_draw_image(p: QtPainterPtr, x: int, y: int, image: QtImagePtr)
extern def vqt6_qpainter_draw_tiled_pixmap(p: QtPainterPtr, x: int, y: int, w: int, h: int, pixmap: QtPixmapPtr)
# 变换
# Transforms
extern def vqt6_qpainter_translate(p: QtPainterPtr, dx: int, dy: int)
extern def vqt6_qpainter_rotate(p: QtPainterPtr, angle: double)
extern def vqt6_qpainter_scale(p: QtPainterPtr, sx: double, sy: double)
@@ -142,7 +142,7 @@ extern def vqt6_qpainter_free(p: QtPainterPtr)
# ============================================================
# QPen — 画笔
# QPen — pen
# ============================================================
extern def vqt6_qpen_create() -> QtPenPtr
extern def vqt6_qpen_create_with_color(color: QtColorPtr) -> QtPenPtr
@@ -157,7 +157,7 @@ extern def vqt6_qpen_free(pen: QtPenPtr)
# ============================================================
# QBrush — 画刷
# QBrush — brush
# ============================================================
extern def vqt6_qbrush_create() -> QtBrushPtr
extern def vqt6_qbrush_create_with_color(color: QtColorPtr) -> QtBrushPtr
@@ -170,7 +170,7 @@ extern def vqt6_qbrush_free(brush: QtBrushPtr)
# ============================================================
# QIcon — 图标
# QIcon — icon
# ============================================================
extern def vqt6_qicon_create() -> QtIconPtr
extern def vqt6_qicon_create_with_file(file_name: QtStringPtr) -> QtIconPtr
@@ -182,7 +182,7 @@ extern def vqt6_qicon_free(icon: QtIconPtr)
# ============================================================
# QCursor — 鼠标光标
# QCursor — mouse cursor
# ============================================================
extern def vqt6_qcursor_create() -> QtCursorPtr
extern def vqt6_qcursor_create_with_shape(shape: int) -> QtCursorPtr
@@ -195,7 +195,7 @@ extern def vqt6_qcursor_free(cursor: QtCursorPtr)
# ============================================================
# QKeyEvent — 键盘事件
# QKeyEvent — key event
# ============================================================
extern def vqt6_qkeyevent_create(type: int, key: int, modifiers: int, text: QtStringPtr) -> QtKeyEventPtr
extern def vqt6_qkeyevent_key(event: QtKeyEventPtr) -> int
@@ -206,7 +206,7 @@ extern def vqt6_qkeyevent_free(event: QtKeyEventPtr)
# ============================================================
# QMouseEvent — 鼠标事件
# QMouseEvent — mouse event
# ============================================================
extern def vqt6_qmouseevent_create(type: int, local_pos: QtObjectPtr, button: int, buttons: int, modifiers: int) -> QtMouseEventPtr
extern def vqt6_qmouseevent_button(event: QtMouseEventPtr) -> int
+16 -16
View File
@@ -7,7 +7,7 @@ from vqt6._types import *
# ============================================================
# ============================================================
# QHostAddress — IP 地址
# QHostAddress — IP address
# ============================================================
def vqt6_qhostaddress_create() -> QtHostAddressPtr | t.State: pass
def vqt6_qhostaddress_create_with_ipv4(ipv4: uint32) -> QtHostAddressPtr | t.State: pass
@@ -26,9 +26,9 @@ def vqt6_qhostaddress_free(addr: QtHostAddressPtr) -> t.State: pass
# ============================================================
# QAbstractSocket — TCP/UDP 套接字基类
# QAbstractSocket — TCP/UDP socket base class
# ============================================================
# 套接字状态
# Socket state
define QT_SOCKET_UNCONNECTED_STATE = 0
define QT_SOCKET_HOST_LOOKUP_STATE = 1
define QT_SOCKET_CONNECTING_STATE = 2
@@ -37,14 +37,14 @@ define QT_SOCKET_BOUND_STATE = 4
define QT_SOCKET_LISTENING_STATE = 5
define QT_SOCKET_CLOSING_STATE = 6
# 套接字类型
# Socket type
define QT_SOCKET_TCP = 0
define QT_SOCKET_UDP = 1
# 套接字错误
# Socket error
define QT_SOCKET_NO_ERROR = 0
# 通用方法
# Common methods
def vqt6_qabstractsocket_state(socket: QtAbstractSocketPtr) -> int | t.State: pass
def vqt6_qabstractsocket_error(socket: QtAbstractSocketPtr) -> int | t.State: pass
def vqt6_qabstractsocket_error_string(socket: QtAbstractSocketPtr) -> QtStringPtr | t.State: pass
@@ -72,7 +72,7 @@ def vqt6_qabstractsocket_local_port(socket: QtAbstractSocketPtr) -> uint16 | t.S
# ============================================================
# QTcpSocket — TCP 套接字
# QTcpSocket — TCP socket
# ============================================================
def vqt6_qtcpsocket_create() -> QtTcpSocketPtr | t.State: pass
def vqt6_qtcpsocket_create_with_parent(parent: QtObjectPtr) -> QtTcpSocketPtr | t.State: pass
@@ -85,7 +85,7 @@ def vqt6_qtcpsocket_abort(socket: QtTcpSocketPtr) -> t.State: pass
# ============================================================
# QUdpSocket — UDP 套接字
# QUdpSocket — UDP socket
# ============================================================
def vqt6_qudpsocket_create() -> QtUdpSocketPtr | t.State: pass
def vqt6_qudpsocket_create_with_parent(parent: QtObjectPtr) -> QtUdpSocketPtr | t.State: pass
@@ -101,7 +101,7 @@ def vqt6_qudpsocket_leave_multicast_group(socket: QtUdpSocketPtr, group_address:
# ============================================================
# QNetworkRequest — 网络请求
# QNetworkRequest — network request
# ============================================================
def vqt6_qnetworkrequest_create() -> QtNetworkRequestPtr | t.State: pass
def vqt6_qnetworkrequest_create_with_url(url: QtStringPtr) -> QtNetworkRequestPtr | t.State: pass
@@ -117,7 +117,7 @@ def vqt6_qnetworkrequest_free(req: QtNetworkRequestPtr) -> t.State: pass
# ============================================================
# QNetworkAccessManager — HTTP 客户端管理器
# QNetworkAccessManager — HTTP client manager
# ============================================================
def vqt6_qnetworkaccessmanager_create() -> QtNetworkAccessManagerPtr | t.State: pass
def vqt6_qnetworkaccessmanager_create_with_parent(parent: QtObjectPtr) -> QtNetworkAccessManagerPtr | t.State: pass
@@ -130,7 +130,7 @@ def vqt6_qnetworkaccessmanager_set_cookie_store(nam: QtNetworkAccessManagerPtr,
# ============================================================
# QNetworkReply — 网络响应
# QNetworkReply — network response
# ============================================================
def vqt6_qnetworkreply_url(reply: QtNetworkReplyPtr) -> QtStringPtr | t.State: pass
def vqt6_qnetworkreply_request(reply: QtNetworkReplyPtr) -> QtNetworkRequestPtr | t.State: pass
@@ -150,18 +150,18 @@ def vqt6_qnetworkreply_set_raw_header(reply: QtNetworkReplyPtr, header_name: cha
# ============================================================
# 套接字类型枚举
# Socket type enums
# ============================================================
# 套接字类型
# Socket type
define QT_ABSTRACT_SOCKET_TCP = 0
define QT_ABSTRACT_SOCKET_UDP = 1
# 网络层协议
# Network-layer protocol
define QT_NETWORK_IPV4_PROTOCOL = 0
define QT_NETWORK_IPV6_PROTOCOL = 1
define QT_NETWORK_ANY_IP_PROTOCOL = 2
# 主机地址特殊情况
# Host address special cases
define QT_HOST_ADDRESS_LOCAL_HOST = 0x00000000
define QT_HOST_ADDRESS_LOCAL_HOST_IPV6 = 0x00000001
define QT_HOST_ADDRESS_BROADCAST = 0x00000002
@@ -170,7 +170,7 @@ define QT_HOST_ADDRESS_ANY_IPV6 = 0x00000005
# ============================================================
# 已知端口常量
# Well-known port constants
# ============================================================
define QT_PORT_FTP = 21
define QT_PORT_SSH = 22
+43 -43
View File
@@ -7,9 +7,9 @@ from vqt6._types import *
# ============================================================
# ============================================================
# QApplication — GUI 应用入口
# QApplication — GUI application entry point
# ============================================================
# argc/argv 通过单独的 buffer 数组传入(Viper 中 C 数组使用指针+长度)
# argc/argv are passed via a separate buffer array (C arrays use pointer+length in Viper)
def vqt6_qapplication_create(argc: int, argv: t.CPtr*) -> QtAppPtr | t.State: pass
def vqt6_qapplication_instance() -> QtAppPtr | t.State: pass
def vqt6_qapplication_exec(app: QtAppPtr) -> int | t.State: pass
@@ -26,7 +26,7 @@ def vqt6_enable_custom_item_checks(app: QtAppPtr) -> t.State: pass
# ============================================================
# QWidget — 所有窗口的基类
# QWidget — base class of all windows
# ============================================================
def vqt6_qwidget_create() -> QtWidgetPtr | t.State: pass
def vqt6_qwidget_create_with_parent(parent: QtWidgetPtr) -> QtWidgetPtr | t.State: pass
@@ -36,7 +36,7 @@ def vqt6_qwidget_close(widget: QtWidgetPtr) -> int | t.State: pass
def vqt6_qwidget_is_visible(widget: QtWidgetPtr) -> int | t.State: pass
def vqt6_qwidget_set_visible(widget: QtWidgetPtr, visible: int) -> t.State: pass
# 几何
# Geometry
def vqt6_qwidget_set_geometry(widget: QtWidgetPtr, x: int, y: int, w: int, h: int) -> t.State: pass
def vqt6_qwidget_geometry(widget: QtWidgetPtr, x: int*, y: int*, w: int*, h: int*) -> t.State: pass
def vqt6_qwidget_resize(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass
@@ -48,51 +48,51 @@ def vqt6_qwidget_height(widget: QtWidgetPtr) -> int | t.State: pass
def vqt6_qwidget_x(widget: QtWidgetPtr) -> int | t.State: pass
def vqt6_qwidget_y(widget: QtWidgetPtr) -> int | t.State: pass
# 文本与标题
# Text and title
def vqt6_qwidget_set_window_title(widget: QtWidgetPtr, title: QtStringPtr) -> t.State: pass
def vqt6_qwidget_window_title(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass
def vqt6_qwidget_set_text(widget: QtWidgetPtr, text: QtStringPtr) -> t.State: pass
def vqt6_qwidget_text(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass
# 启用
# Enabled state
def vqt6_qwidget_set_enabled(widget: QtWidgetPtr, enabled: int) -> t.State: pass
def vqt6_qwidget_is_enabled(widget: QtWidgetPtr) -> int | t.State: pass
# 父对象
# Parent
def vqt6_qwidget_set_parent(widget: QtWidgetPtr, parent: QtWidgetPtr) -> t.State: pass
def vqt6_qwidget_parent_widget(widget: QtWidgetPtr) -> QtWidgetPtr | t.State: pass
# 焦点
# Focus
def vqt6_qwidget_set_focus(widget: QtWidgetPtr) -> t.State: pass
def vqt6_qwidget_clear_focus(widget: QtWidgetPtr) -> t.State: pass
def vqt6_qwidget_has_focus(widget: QtWidgetPtr) -> int | t.State: pass
def vqt6_qwidget_set_focus_policy(widget: QtWidgetPtr, policy: int) -> t.State: pass
# 更新
# Update
def vqt6_qwidget_update(widget: QtWidgetPtr) -> t.State: pass
def vqt6_qwidget_repaint(widget: QtWidgetPtr) -> t.State: pass
def vqt6_qwidget_set_update_enabled(widget: QtWidgetPtr, enabled: int) -> t.State: pass
# 窗口标志
# Window flags
def vqt6_qwidget_set_window_flags(widget: QtWidgetPtr, flags: int) -> t.State: pass
def vqt6_qwidget_window_flags(widget: QtWidgetPtr) -> int | t.State: pass
# 鼠标光标
# Mouse cursor
def vqt6_qwidget_set_cursor(widget: QtWidgetPtr, cursor: QtCursorPtr) -> t.State: pass
def vqt6_qwidget_cursor(widget: QtWidgetPtr) -> QtCursorPtr | t.State: pass
def vqt6_qwidget_unset_cursor(widget: QtWidgetPtr) -> t.State: pass
# 工具提示
# Tooltip
def vqt6_qwidget_set_tool_tip(widget: QtWidgetPtr, tip: QtStringPtr) -> t.State: pass
# Qt 样式表(QSS):直接传 Viper str
# Qt stylesheet (QSS): pass the Viper str directly
def vqt6_qwidget_set_stylesheet_utf8(widget: QtWidgetPtr, qss: char*) -> t.State: pass
def vqt6_qwidget_tool_tip(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass
# 状态栏提示
# Status bar tip
def vqt6_qwidget_set_status_tip(widget: QtWidgetPtr, tip: QtStringPtr) -> t.State: pass
def vqt6_qwidget_status_tip(widget: QtWidgetPtr) -> QtStringPtr | t.State: pass
# 最小/最大尺寸
# Minimum/maximum size
def vqt6_qwidget_set_minimum_size(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass
def vqt6_qwidget_set_maximum_size(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass
def vqt6_qwidget_set_fixed_size(widget: QtWidgetPtr, w: int, h: int) -> t.State: pass
@@ -101,7 +101,7 @@ def vqt6_qwidget_maximum_size(widget: QtWidgetPtr, w: int*, h: int*) -> t.State:
# ============================================================
# QDialog — 对话框
# QDialog — dialog
# ============================================================
def vqt6_qdialog_create() -> QtDialogPtr | t.State: pass
def vqt6_qdialog_create_with_parent(parent: QtWidgetPtr) -> QtDialogPtr | t.State: pass
@@ -116,7 +116,7 @@ def vqt6_qdialog_is_modal(dialog: QtDialogPtr) -> int | t.State: pass
# ============================================================
# QMainWindow — 主窗口
# QMainWindow — main window
# ============================================================
def vqt6_qmainwindow_create() -> QtMainWindowPtr | t.State: pass
def vqt6_qmainwindow_create_with_parent(parent: QtWidgetPtr) -> QtMainWindowPtr | t.State: pass
@@ -131,13 +131,13 @@ def vqt6_qmainwindow_add_tool_bar_break(mw: QtMainWindowPtr) -> t.State: pass
# ============================================================
# QLabel — 标签
# QLabel — label
# ============================================================
def vqt6_qlabel_create() -> QtLabelPtr | t.State: pass
def vqt6_qlabel_create_with_text(text: QtStringPtr) -> QtLabelPtr | t.State: pass
def vqt6_qlabel_create_with_text_parent(text: QtStringPtr, parent: QtWidgetPtr) -> QtLabelPtr | t.State: pass
def vqt6_qlabel_set_text(label: QtLabelPtr, text: QtStringPtr) -> t.State: pass
# UTF-8 便捷版:直接传 Viper str,避免手动创建/释放 QString
# UTF-8 convenience: pass the Viper str directly, no manual QString create/free
def vqt6_qlabel_set_text_utf8(label: QtLabelPtr, text: char*) -> t.State: pass
def vqt6_qlabel_text(label: QtLabelPtr) -> QtStringPtr | t.State: pass
def vqt6_qlabel_set_alignment(label: QtLabelPtr, alignment: int) -> t.State: pass
@@ -151,7 +151,7 @@ def vqt6_qlabel_word_wrap(label: QtLabelPtr) -> int | t.State: pass
# ============================================================
# QPushButton — 按钮
# QPushButton — button
# ============================================================
def vqt6_qpushbutton_create() -> QtPushButtonPtr | t.State: pass
def vqt6_qpushbutton_create_with_text(text: QtStringPtr) -> QtPushButtonPtr | t.State: pass
@@ -172,7 +172,7 @@ def vqt6_qpushbutton_click(btn: QtPushButtonPtr) -> t.State: pass
# ============================================================
# QLineEdit — 单行输入框
# QLineEdit — single-line input
# ============================================================
def vqt6_qlineedit_create() -> QtLineEditPtr | t.State: pass
def vqt6_qlineedit_create_with_text(text: QtStringPtr) -> QtLineEditPtr | t.State: pass
@@ -192,7 +192,7 @@ def vqt6_qlineedit_clear(le: QtLineEditPtr) -> t.State: pass
# ============================================================
# QTextEdit / QPlainTextEdit — 多行文本编辑器
# QTextEdit / QPlainTextEdit — multi-line text editor
# ============================================================
def vqt6_qtextedit_create() -> QtTextEditPtr | t.State: pass
def vqt6_qtextedit_create_with_parent(parent: QtWidgetPtr) -> QtTextEditPtr | t.State: pass
@@ -210,7 +210,7 @@ def vqt6_qplaintextedit_create_with_parent(parent: QtWidgetPtr) -> QtPlainTextEd
def vqt6_qplaintextedit_to_plain_text(pte: QtPlainTextEditPtr) -> QtStringPtr | t.State: pass
def vqt6_qplaintextedit_set_plain_text(pte: QtPlainTextEditPtr, text: QtStringPtr) -> t.State: pass
def vqt6_qplaintextedit_append(pte: QtPlainTextEditPtr, text: QtStringPtr) -> t.State: pass
# UTF-8 便捷版:直接传 Viper str,避免手动创建/释放 QString
# UTF-8 convenience: pass the Viper str directly, no manual QString create/free
def vqt6_qplaintextedit_append_utf8(pte: QtPlainTextEditPtr, text: char*) -> t.State: pass
def vqt6_qplaintextedit_set_plain_text_utf8(pte: QtPlainTextEditPtr, text: char*) -> t.State: pass
def vqt6_qplaintextedit_clear(pte: QtPlainTextEditPtr) -> t.State: pass
@@ -219,7 +219,7 @@ def vqt6_qplaintextedit_line_count(pte: QtPlainTextEditPtr) -> int | t.State: pa
# ============================================================
# QCheckBox / QRadioButton — 复选/单选
# QCheckBox / QRadioButton — check/radio
# ============================================================
def vqt6_qcheckbox_create() -> QtCheckBoxPtr | t.State: pass
def vqt6_qcheckbox_create_with_text(text: QtStringPtr) -> QtCheckBoxPtr | t.State: pass
@@ -241,7 +241,7 @@ def vqt6_qradiobutton_text(rb: QtRadioButtonPtr) -> QtStringPtr | t.State: pass
# ============================================================
# QComboBox — 下拉框
# QComboBox — combo box
# ============================================================
def vqt6_qcombobox_create() -> QtComboBoxPtr | t.State: pass
def vqt6_qcombobox_create_with_parent(parent: QtWidgetPtr) -> QtComboBoxPtr | t.State: pass
@@ -260,7 +260,7 @@ def vqt6_qcombobox_is_editable(cb: QtComboBoxPtr) -> int | t.State: pass
# ============================================================
# QListWidget — 列表控件
# QListWidget — list widget
# ============================================================
def vqt6_qlistwidget_create() -> QtListWidgetPtr | t.State: pass
def vqt6_qlistwidget_create_with_parent(parent: QtWidgetPtr) -> QtListWidgetPtr | t.State: pass
@@ -276,11 +276,11 @@ def vqt6_qlistwidget_set_current_row(lw: QtListWidgetPtr, row: int) -> t.State:
def vqt6_qlistwidget_current_item_text(lw: QtListWidgetPtr) -> QtStringPtr | t.State: pass
def vqt6_qlistwidget_item_text(lw: QtListWidgetPtr, row: int) -> QtStringPtr | t.State: pass
def vqt6_qlistwidget_set_item_text(lw: QtListWidgetPtr, row: int, text: QtStringPtr) -> t.State: pass
# 聊天气泡行:右/左对齐气泡(setItemWidget 实现);hpad/vpad = QSS padding 左右/上下合计像素
# Chat bubble row: right/left aligned bubble (via setItemWidget); hpad/vpad = QSS padding h/v total px
def vqt6_qlistwidget_add_bubble(lw: QtListWidgetPtr, text: QtStringPtr, qss: QtStringPtr,
right_align: int, max_bubble_w: int,
hpad: int, vpad: int) -> t.State: pass
# 流式刷新:删除最后一条气泡后按新文本重建
# Streaming refresh: drop the last bubble and rebuild it from new text
def vqt6_qlistwidget_replace_last_bubble(lw: QtListWidgetPtr, text: QtStringPtr, qss: QtStringPtr,
right_align: int, max_bubble_w: int,
hpad: int, vpad: int) -> t.State: pass
@@ -288,7 +288,7 @@ def vqt6_qlistwidget_scroll_to_bottom(lw: QtListWidgetPtr) -> t.State: pass
# ============================================================
# QTreeWidget / QTreeWidgetItem — 树状控件
# QTreeWidget / QTreeWidgetItem — tree widget
# ============================================================
def vqt6_qtreewidget_create() -> QtTreeWidgetPtr | t.State: pass
def vqt6_qtreewidget_create_with_parent(parent: QtWidgetPtr) -> QtTreeWidgetPtr | t.State: pass
@@ -319,7 +319,7 @@ def vqt6_qtreewidgetitem_data_int(item: QtTreeWidgetItemPtr) -> int | t.State: p
# ============================================================
# QProgressBar — 进度条
# QProgressBar — progress bar
# ============================================================
def vqt6_qprogressbar_create() -> QtProgressBarPtr | t.State: pass
def vqt6_qprogressbar_create_with_parent(parent: QtWidgetPtr) -> QtProgressBarPtr | t.State: pass
@@ -336,7 +336,7 @@ def vqt6_qprogressbar_reset(pb: QtProgressBarPtr) -> t.State: pass
# ============================================================
# QSlider — 滑块
# QSlider — slider
# ============================================================
def vqt6_qslider_create() -> QtSliderPtr | t.State: pass
def vqt6_qslider_create_with_orientation(orientation: int) -> QtSliderPtr | t.State: pass
@@ -353,7 +353,7 @@ def vqt6_qslider_set_page_step(s: QtSliderPtr, step: int) -> t.State: pass
# ============================================================
# QSpinBox — 数字输入框
# QSpinBox — number input
# ============================================================
def vqt6_qspinbox_create() -> QtSpinBoxPtr | t.State: pass
def vqt6_qspinbox_create_with_parent(parent: QtWidgetPtr) -> QtSpinBoxPtr | t.State: pass
@@ -372,7 +372,7 @@ def vqt6_qspinbox_suffix(sb: QtSpinBoxPtr) -> QtStringPtr | t.State: pass
# ============================================================
# 布局 - QLayout / QBoxLayout / QGridLayout / QFormLayout
# Layouts - QLayout / QBoxLayout / QGridLayout / QFormLayout
# ============================================================
def vqt6_qlayout_add_widget(layout: QtLayoutPtr, widget: QtWidgetPtr) -> t.State: pass
def vqt6_qlayout_remove_widget(layout: QtLayoutPtr, widget: QtWidgetPtr) -> t.State: pass
@@ -498,7 +498,7 @@ def vqt6_qtoolbar_orientation(tb: QtToolBarPtr) -> int | t.State: pass
# ============================================================
# QMessageBox — 消息对话框(静态方法)
# QMessageBox — message dialog (static methods)
# ============================================================
def vqt6_qmessagebox_information(parent: QtWidgetPtr, title: QtStringPtr, text: QtStringPtr, buttons: int) -> int | t.State: pass
def vqt6_qmessagebox_warning(parent: QtWidgetPtr, title: QtStringPtr, text: QtStringPtr, buttons: int) -> int | t.State: pass
@@ -509,7 +509,7 @@ def vqt6_qmessagebox_about_qt(parent: QtWidgetPtr) -> t.State: pass
# ============================================================
# QFileDialog — 文件选择对话框(静态方法)
# QFileDialog — file dialog (static methods)
# ============================================================
def vqt6_qfiledialog_get_open_file_name(parent: QtWidgetPtr, caption: QtStringPtr, dir: QtStringPtr, filter: QtStringPtr) -> QtStringPtr | t.State: pass
def vqt6_qfiledialog_get_save_file_name(parent: QtWidgetPtr, caption: QtStringPtr, dir: QtStringPtr, filter: QtStringPtr) -> QtStringPtr | t.State: pass
@@ -518,7 +518,7 @@ def vqt6_qfiledialog_get_open_file_names(parent: QtWidgetPtr, caption: QtStringP
# ============================================================
# QColorDialog / QFontDialog / QInputDialog — 各种对话框
# QColorDialog / QFontDialog / QInputDialog — assorted dialogs
# ============================================================
def vqt6_qcolordialog_get_color(initial: QtColorPtr, parent: QtWidgetPtr, title: QtStringPtr) -> QtColorPtr | t.State: pass
def vqt6_qfontdialog_get_font(initial: QtFontPtr, parent: QtWidgetPtr, title: QtStringPtr) -> QtFontPtr | t.State: pass
@@ -529,8 +529,8 @@ def vqt6_qinputdialog_get_item(parent: QtWidgetPtr, title: QtStringPtr, label: Q
# ============================================================
# 信号连接(Qt 信号 → Viper 回调)
# 回调统一签名: def cb(slot_id: int, user_data: t.CPtr)
# Signal connections (Qt signal -> Viper callback)
# Unified callback signature: def cb(slot_id: int, user_data: t.CPtr)
# ============================================================
def vqt6_qpushbutton_connect_clicked(btn: QtPushButtonPtr, cb: bytes, user_data: bytes) -> t.State: pass
def vqt6_qpushbutton_connect_toggled(btn: QtPushButtonPtr, cb: bytes, user_data: bytes) -> t.State: pass
@@ -550,8 +550,8 @@ def vqt6_qtreewidget_connect_item_clicked(tw: QtTreeWidgetPtr, cb: bytes, user_d
# ============================================================
# Canvas — 手绘板 / 28x28 灰度显示(自绘控件)
# 坐标约定: 内部 0..1 归一化, 与控件像素尺寸解耦
# Canvas — freehand board / 28x28 grayscale display (custom-painted widget)
# Coordinate convention: normalized 0..1 internally, decoupled from pixel size
# ============================================================
def vqt6_canvas_create() -> QtCanvasPtr | t.State: pass
def vqt6_canvas_create_with_parent(parent: QtWidgetPtr) -> QtCanvasPtr | t.State: pass
@@ -569,8 +569,8 @@ def vqt6_canvas_connect_changed(canvas: QtCanvasPtr, cb: bytes, user_data: bytes
# ============================================================
# Graph3D — 软件 3D 神经网络结构图(自绘控件)
# API 与渲染后端无关, 后续可替换为 OpenGL 实现
# Graph3D — software 3D neural-network structure view (custom-painted widget)
# Renderer-backend independent; can later be replaced by an OpenGL implementation
# ============================================================
def vqt6_graph3d_create() -> QtGraph3DPtr | t.State: pass
def vqt6_graph3d_create_with_parent(parent: QtWidgetPtr) -> QtGraph3DPtr | t.State: pass
@@ -593,7 +593,7 @@ def vqt6_graph3d_reset_view(g: QtGraph3DPtr) -> t.State: pass
# ============================================================
# ChartView — Qt Charts 折线图(loss/acc 曲线)
# ChartView — Qt Charts line chart (loss/acc curves)
# ============================================================
def vqt6_chartview_create() -> QtChartViewPtr | t.State: pass
def vqt6_chartview_create_with_parent(parent: QtWidgetPtr) -> QtChartViewPtr | t.State: pass
+11 -11
View File
@@ -8,8 +8,8 @@ from stdint import *
# 不暴露 C++ 类的内部布局,只暴露指针
# ============================================================
# 不透明指针 typedef(Qt6 C++ 类的 Viper 端表示)
QtObjectPtr: t.CTypedef = t.CPtr # void* 通用不透明指针
# Opaque pointer typedefs (Viper-side representation of Qt6 C++ classes)
QtObjectPtr: t.CTypedef = t.CPtr # void* generic opaque pointer
QtStringPtr: t.CTypedef = t.CPtr # QString*
QtByteArrayPtr: t.CTypedef = t.CPtr # QByteArray*
QtVariantPtr: t.CTypedef = t.CPtr # QVariant*
@@ -80,9 +80,9 @@ QtColorDialogPtr: t.CTypedef = t.CPtr # QColorDialog*
QtFontDialogPtr: t.CTypedef = t.CPtr # QFontDialog*
QtInputDialogPtr: t.CTypedef = t.CPtr # QInputDialog*
# vqt6 自绘控件(_bridge_canvas)
QtCanvasPtr: t.CTypedef = t.CPtr # Vqt6Canvas*(QWidget* 兼容)
QtGraph3DPtr: t.CTypedef = t.CPtr # Vqt6Graph3D*(QWidget* 兼容)
# vqt6 custom-painted widgets (_bridge_canvas)
QtCanvasPtr: t.CTypedef = t.CPtr # Vqt6Canvas* (QWidget*-compatible)
QtGraph3DPtr: t.CTypedef = t.CPtr # Vqt6Graph3D* (QWidget*-compatible)
QtChartViewPtr: t.CTypedef = t.CPtr # QChartView*
QtChartSeriesPtr: t.CTypedef = t.CPtr # QLineSeries*
@@ -96,10 +96,10 @@ QtNetworkReplyPtr: t.CTypedef = t.CPtr # QNetworkReply*
QtNetworkRequestPtr: t.CTypedef = t.CPtr # QNetworkRequest*
# ============================================================
# Qt 通用常量(C++ 枚举值的 C 兼容定义)
# Qt common constants (C-compatible definitions of C++ enum values)
# ============================================================
# Qt 键盘按键 (Qt::Key)
# Qt key codes (Qt::Key)
define QT_KEY_ESCAPE = 0x01000000
define QT_KEY_TAB = 0x01000001
define QT_KEY_BACKTAB = 0x01000002
@@ -131,7 +131,7 @@ define QT_KEY_Z = 0x5a
define QT_KEY_0 = 0x30
define QT_KEY_9 = 0x39
# Qt 鼠标按键 (Qt::MouseButton)
# Qt mouse buttons (Qt::MouseButton)
define QT_NO_BUTTON = 0x00000000
define QT_LEFT_BUTTON = 0x00000001
define QT_RIGHT_BUTTON = 0x00000002
@@ -150,7 +150,7 @@ define QT_ALIGN_CENTER = QT_ALIGN_HCENTER | QT_ALIGN_VCENTER
define QT_HORIZONTAL = 0x1
define QT_VERTICAL = 0x2
# Qt 焦点
# Qt focus
define QT_NO_FOCUS = 0x00
define QT_TAB_FOCUS = 0x01
define QT_CLICK_FOCUS = 0x02
@@ -174,14 +174,14 @@ define QT_WINDOW_MIN_MAX_BUTTONS = 0x00000004
define QT_WINDOW_CLOSE_BUTTON = 0x08000000
define QT_WINDOW_CONTEXT_HELP_BUTTON = 0x00000008
# QMessageBox 标准按钮
# QMessageBox standard buttons
define QT_OK = 0x00000400
define QT_CANCEL = 0x00400000
define QT_YES = 0x00004000
define QT_NO = 0x00010000
define QT_CLOSE = 0x00200000
# 事件类型
# Event types
define QT_EVENT_NONE = 0
define QT_EVENT_KEY = 6
define QT_EVENT_MOUSE_BUTTON_PRESS = 2
+19 -47
View File
@@ -38,40 +38,29 @@ define SHARE_WRITE = 0x00000002
define SHARE_DELETE = 0x00000004
# =============================================================================
# 环境变量展开(路径层唯一实现)
# Environment variable expansion (the only implementation in the path layer).
#
# 用途:工程配置里的路径可写成 %temp%/viperepl/.tpv_build 这类形式,需在打开
# 文件前展开为实路径。ExpandEnvironmentStringsA 由 kernel32 提供,与
# SetConsoleOutputCP 同属进程启动即可用的 API(无额外初始化时序要求)。
#
# 安全约束(保证既有路径语义逐字不变):
# * 只在路径含 '%' 时才调用 Win32(无 '%' 的绝大多数路径零开销);
# * 展开失败(返回 0)或结果长度超过缓冲上限(返回所需长度 > 上限,此时
# 目标缓冲内容不可用)一律返回 None,调用方沿用原路径;
# * 不做任何前缀/分隔符规范化:UNC(\\server\share)、相对路径、盘符、
# ".." 段、长路径都原样交给 Win32,展开成功时也只替换 %VAR% 片段;
# * A 系列展开结果与 CreateDirectoryA / CreateProcessA / FindFirstFileA 等
# 既有 A 系列调用点同编码(系统 ANSI 代码页),不会引入编码错配。
# 返回 stdlib.malloc 的新串(调用方负责 stdlib.free);无需展开或失败返回 None。
# 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
# 展开结果缓冲上限(含 NUL;超过即视为失败并退回原路径)
# 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:
"""路径含 '%' 时按环境变量展开:返回新串(调用方 stdlib.free);无需/失败返回 None"""
"""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
# 快速路径:无 '%' 直接返回 None(不调用 Win32,路径逐字不变)
# 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:失败;n > 上限:结果所需的 TCHAR 数(含 NUL)超过缓冲,内容不可用
# 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
@@ -80,11 +69,8 @@ def ExpandEnvironmentPath(path: str) -> str:
def create_file_utf8(path: str, access: ULONG, share: ULONG, disposition: ULONG,
flags: ULONG) -> w32.win32base.HANDLE:
"""按路径编码打开/创建文件。
含 '%' 的路径先做环境变量展开(ExpandEnvironmentPath,失败则原样使用);
合法 UTF-8(源码字面量、Qt 返回的中文目录) → 转 UTF-16 走 CreateFileW;
非法 UTF-8(如命令行 argv 的 ANSI 串) → 退回 CreateFileA。详见 win32base"""
# 展开结果的生命周期覆盖到 CreateFile 调用之后(句柄由内核持有,串用完即弃)
"""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:
@@ -96,8 +82,8 @@ def create_file_utf8(path: str, access: ULONG, share: ULONG, disposition: ULONG,
else:
wp: w32.win32base.WCHARPTR = w32.win32base.utf8_to_utf16_dup(use_path)
if wp is None:
# 宽字符缓冲分配失败: 走 A 路径, 非 ASCII 路径在此自然失败并返回
# INVALID_HANDLE_VALUE(不掩盖错误, 只是复用同一失败出口)
# 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:
@@ -255,9 +241,8 @@ class File:
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
# 明确字节读:下标访问按指针元素类型(i8)load 1 字节(Repl.vp
# _read_line 同款)。不用 c.Deref(buf + total)——后者的宽度取决于
# 指针类型是否为 i8*,此处语义明确要求 1 字节。
# 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
@@ -266,14 +251,8 @@ class File:
return LONG(total)
def read_all(self, buf: bytes, buf_size: ULONG) -> LONG:
"""把整个文件读进 buf 并写成 C 字符串(内部先 seek 到 0)
契约(注意与 write 的 count 口径不同):
buf_size 是缓冲区**容量, 含终止符** —— 最多读 buf_size-1 字节,
再在 buf[返回值] 写一个 NUL 字节, 故返回值 <= buf_size-1。
文件比 buf_size-1 更长时**静默截断**, 不报错; 要区分"读完了"还是
"被截断了", 请在调用前自己比一次 f.size()。
返回实际读到的字节数, 负数为错误码。"""
"""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
@@ -439,9 +418,8 @@ class FileW:
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
# 明确字节读:下标访问按指针元素类型(i8)load 1 字节(Repl.vp
# _read_line 同款)。不用 c.Deref(buf + total)——后者的宽度取决于
# 指针类型是否为 i8*,此处语义明确要求 1 字节。
# 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
@@ -450,14 +428,8 @@ class FileW:
return LONG(total)
def read_all(self, buf: bytes, buf_size: ULONG) -> LONG:
"""把整个文件读进 buf 并写成 C 字符串(内部先 seek 到 0)
契约(注意与 write 的 count 口径不同):
buf_size 是缓冲区**容量, 含终止符** —— 最多读 buf_size-1 字节,
再在 buf[返回值] 写一个 NUL 字节, 故返回值 <= buf_size-1。
文件比 buf_size-1 更长时**静默截断**, 不报错; 要区分"读完了"还是
"被截断了", 请在调用前自己比一次 f.size()。
返回实际读到的字节数, 负数为错误码。"""
"""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
+4 -8
View File
@@ -82,11 +82,8 @@ extern def GetCommandLineA() -> CHARPTR
# =============================================================================
# 路径编码: Viper 字符串统一 UTF-8, 而 Windows 的 A 系列 API 走系统 ANSI 代码页
# (中文机器上为 GBK)。直接拿 UTF-8 路径调 CreateFileA / CreateDirectoryA,
# 遇到中文目录名会被按 GBK 误读 → 误报"文件/目录不存在"。故路径统一转 UTF-16
# 走 W 系列 API; 仅当字节串不是合法 UTF-8(如命令行 argv 传来的 ANSI 串)时才
# 退回 A 系列, 保持既有行为。
# Path encoding: Viper strings are UTF-8 but Windows A APIs use the ANSI code page, so non-ASCII UTF-8 paths would be misread; convert paths to UTF-16 and use W APIs.
# Fall back to A APIs only when the bytes are not valid UTF-8 (e.g. ANSI argv), preserving existing behavior.
# =============================================================================
define UTF8_CODEPAGE = 65001
define MB_ERR_INVALID_CHARS = 0x00000008
@@ -97,7 +94,7 @@ extern def MultiByteToWideChar(CodePage: UINT, dwFlags: DWORD, lpMultiByteStr: L
def utf8_is_valid(s: str) -> int:
"""整串(含结尾 NUL)是否为合法 UTF-8。1=是(按 UTF-8 处理), 0=否(按 ANSI)"""
"""Whether the whole string (incl. trailing NUL) is valid UTF-8. 1=yes (treat as UTF-8), 0=no (ANSI)."""
if s is None:
return 0
n: int = MultiByteToWideChar(UTF8_CODEPAGE, MB_ERR_INVALID_CHARS, s, -1,
@@ -108,8 +105,7 @@ def utf8_is_valid(s: str) -> int:
def utf8_to_utf16_dup(s: str) -> WCHARPTR:
"""UTF-8 → 新分配的 UTF-16 缓冲(含结尾 NUL), 调用方负责 stdlib.free;
失败(含非法 UTF-8)返回 None"""
"""UTF-8 -> newly allocated UTF-16 buffer (incl. trailing NUL); caller must stdlib.free; returns None on failure (incl. invalid UTF-8)."""
if s is None:
return None
n: int = MultiByteToWideChar(UTF8_CODEPAGE, 0, s, -1, t.CPtr(0), 0)
+6 -10
View File
@@ -32,12 +32,12 @@ define WINDOW_BUFFER_SIZE_EVENT = 0x0004
define MENU_EVENT = 0x0008
define FOCUS_EVENT = 0x0010
# 控制事件类型(SetConsoleCtrlHandler 处理例程的入参 dwCtrlType;consoleapi.h)
# Control event types (dwCtrlType arg of the SetConsoleCtrlHandler routine; consoleapi.h)
define CTRL_C_EVENT = 0# Ctrl+C
define CTRL_BREAK_EVENT = 1# Ctrl+Break
define CTRL_CLOSE_EVENT = 2# 关闭控制台窗口
define CTRL_LOGOFF_EVENT = 5# 用户注销
define CTRL_SHUTDOWN_EVENT = 6# 系统关机
define CTRL_CLOSE_EVENT = 2# console window closed
define CTRL_LOGOFF_EVENT = 5# user logoff
define CTRL_SHUTDOWN_EVENT = 6# system shutdown
class COORD():
X: SHORT
@@ -117,10 +117,6 @@ extern def FreeConsole() -> BOOL
extern def SetConsoleWindowInfo(hConsoleOutput: HANDLE, bAbsolute: BOOL, lpConsoleWindow: SMALL_RECT*) -> BOOL
extern def ScrollConsoleScreenBufferA(hConsoleOutput: HANDLE, lpScrollRectangle: SMALL_RECT*, lpClipRectangle: SMALL_RECT*, dwDestinationOrigin: COORD, lpFill: CHAR_INFO*) -> BOOL
# 控制事件处理例程注册/摘除(consoleapi.h:114-115 / kernel32.dll,本仓由 -lkernel32 链接)
# BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add);
# typedef BOOL (WINAPI *PHANDLER_ROUTINE)(DWORD dwCtrlType);
# 第一个形参按本仓"函数指针用 t.CPtr 传"的写法声明:实参直接写处理例程函数名,
# 由 TPV 把函数引用 bitcast 成函数指针(先例:VET/App/lib/VGuard.vp:319/896 同签名)。
# 返回 0 = 失败(GetLastError 取原因);Add=FALSE 时摘除已注册的例程。
# SetConsoleCtrlHandler registration/removal (consoleapi.h / kernel32.dll): pass the routine name directly, bitcast by TPV (function pointers declared as t.CPtr).
# Returns 0 on failure (see GetLastError); Add=FALSE removes the registered routine.
extern def SetConsoleCtrlHandler(HandlerRoutine: t.CPtr, Add: BOOL) -> BOOL
+24 -37
View File
@@ -32,16 +32,13 @@ from w32.win32base import *
# 三者只用于传给 StackWalk64(它按自身定义写入),故尺寸/偏移必须与 C 侧逐字节一致。
# =============================================================================
# IMAGE_FILE_MACHINE_* (winnt.h;x64 = 0x8664)
# IMAGE_FILE_MACHINE_* (winnt.h; x64 = 0x8664)
define IMAGE_FILE_MACHINE_AMD64 = 0x8664
# ADDRESS_MODE(dbghelp.h):AddrMode1616=0 / AddrMode1632=1 / AddrModeReal=2 / AddrModeFlat=3
# ADDRESS_MODE (dbghelp.h): AddrMode1616=0 / AddrMode1632=1 / AddrModeReal=2 / AddrModeFlat=3
define ADDR_MODE_FLAT = 3
# SYMOPT_*(dbghelp.h)——只列本仓用到的四项:
# DEFERRED_LOADS:SymInitialize(fInvadeProcess=TRUE) 时**只枚举模块、不加载符号/PDB**
# (模块枚举是 StackWalk64 的必要前提;符号本身本模块不需要);
# UNDNAME:修饰名解码为可读名(本模块只取地址,仅为了不改变 dbghelp 的默认语义);
# NO_PROMPTS:禁止 dbghelp 弹交互式对话框(否则缺符号时可能卡住进程启动)。
# SYMOPT_* (dbghelp.h), only the three used here:
# DEFERRED_LOADS enumerates modules without loading symbols/PDB; UNDNAME keeps default name decoding; NO_PROMPTS forbids interactive dialogs (missing symbols can otherwise hang startup).
define SYMOPT_UNDNAME = 0x00000002
define SYMOPT_DEFERRED_LOADS = 0x00000004
define SYMOPT_NO_PROMPTS = 0x00080000
@@ -49,9 +46,9 @@ define SYMOPT_NO_PROMPTS = 0x00080000
# typedef struct _ADDRESS64 { DWORD64 Offset; WORD Segment; ADDRESS_MODE Mode; } ADDRESS64;
class ADDRESS64():
Offset: ULONGLONG # 0x00 地址(PC/帧/栈指针)
Segment: WORD # 0x08 段选择子(x64 恒 0)
Mode: DWORD # 0x0C 地址模式(ADDR_MODE_FLAT=3;enum 按 i32 占位)
Offset: ULONGLONG # 0x00 address (PC/frame/stack pointer)
Segment: WORD # 0x08 segment selector (always 0 on x64)
Mode: DWORD # 0x0C address mode (ADDR_MODE_FLAT=3; enum occupies i32)
# typedef struct _KDHELP64 {...} KDHELP64;
@@ -70,52 +67,43 @@ class KDHELP64():
BuildVersion: ULONGLONG # 0x48
Reserved0: ULONGLONG # 0x50
Reserved1: ULONGLONG # 0x58
# 合计 0x60 = 96 字节
# total 0x60 = 96 bytes
# typedef struct _STACKFRAME64 {...} STACKFRAME64;
# 调用约定:调用方只需填 AddrPC.Offset/AddrFrame.Offset/AddrStack.Offset + 三者 Mode=3,
# 其余字段由 StackWalk64 内部维护(第一次调用前应整块清零)。
# ⚠ 本结构**没有**任何"结构体尺寸/字段数"成员(NumFieldsInStruct 是堆栈遍历的
# STACKFRAME_EX / StackWalkEx 才有的概念)。StackWalk64 按自己的 dbghelp.h 定义
# 读写本结构,调用方无需、也无法填尺寸;故本仓只需保证 sizeof 与 C 侧一致
# (x64 实测 sizeof(STACKFRAME64) = 0xF8 = 248 字节,见下方字段偏移)。
# Caller must set AddrPC/AddrFrame/AddrStack .Offset plus Mode=3 on all three; other fields are maintained by StackWalk64 (zero the whole struct before the first call).
# This struct has no NumFieldsInStruct member (that is STACKFRAME_EX/StackWalkEx only); StackWalk64 reads/writes it by its own dbghelp.h, so only sizeof must match C (x64: 0xF8 = 248 bytes).
class STACKFRAME64():
AddrPC: ADDRESS64 # 0x000 当前指令指针
AddrReturn: ADDRESS64 # 0x010 返回地址
AddrFrame: ADDRESS64 # 0x020 帧指针(x64 = RBP)
AddrStack: ADDRESS64 # 0x030 栈指针(x64 = RSP)
AddrBStore: ADDRESS64 # 0x040 (x86 用;x64 恒 0)
AddrPC: ADDRESS64 # 0x000 current instruction pointer
AddrReturn: ADDRESS64 # 0x010 return address
AddrFrame: ADDRESS64 # 0x020 frame pointer (RBP on x64)
AddrStack: ADDRESS64 # 0x030 stack pointer (RSP on x64)
AddrBStore: ADDRESS64 # 0x040 (x86 only; always 0 on x64)
FuncTableEntry: VOIDPTR # 0x050
Params: t.CArray[uint64, 4] # 0x058
Far: BOOL # 0x078
Virtual: BOOL # 0x07C
Reserved: t.CArray[uint64, 3] # 0x080
KdHelp: KDHELP64 # 0x098
# 合计 0xF8 = 248 字节
# total 0xF8 = 248 bytes
# =============================================================================
# dbghelp.dll 导出(dbghelp.h / dbghelp.lib)
# dbghelp.dll exports (dbghelp.h / dbghelp.lib)
# =============================================================================
# DWORD SymSetOptions(DWORD SymOptions);
# 必须在 SymInitialize **之前**调用(调用后初始化即固定该选项集)。
# Must be called before SymInitialize (the option set is fixed at initialization).
extern def SymSetOptions(SymOptions: DWORD) -> DWORD
# BOOL SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess);
# fInvadeProcess=TRUE:枚举进程已加载模块并登记进 dbghelp 的模块表。**这是
# StackWalk64 的硬前提**——fInvadeProcess=FALSE 时模块表为空,SymFunctionTableAccess64
# 查不到 RUNTIME_FUNCTION(返回 NULL)、SymGetModuleBase64 返回 0,StackWalk64 会
# 首次调用即失败、一帧都走不出。本模块不需要 PDB(只取返回地址),配合
# SYMOPT_DEFERRED_LOADS 时枚举模块不会真的去加载任何符号文件。
# fInvadeProcess=TRUE enumerates loaded modules into dbghelp's module table; this is required by StackWalk64.
# With fInvadeProcess=FALSE the table is empty: SymFunctionTableAccess64 returns NULL and StackWalk64 fails on the first frame. PDBs are not needed; with SYMOPT_DEFERRED_LOADS module enumeration loads no symbol files.
extern def SymInitialize(hProcess: HANDLE, UserSearchPath: LPCSTR, fInvadeProcess: BOOL) -> BOOL
# BOOL SymRefreshModuleList(HANDLE hProcess);
# DbgHelp 的进程上下文是**进程级**状态、按句柄唯一:同一句柄重复 SymInitialize 会被拒
# (本仓实测 ASan 运行时先注册了自己的符号化器,故本模块的 SymInitialize 失败)。这不是
# 冲突——状态是共享的,用同一个 GetCurrentProcess() 伪句柄调用本函数即可按其既有上下文
# 重新枚举模块表,使 StackWalk64 拿到最新的 RUNTIME_FUNCTION 表。用法见 traceback.vp 的 Install。
# DbgHelp's process context is process-wide and per-handle: calling SymInitialize twice on the same handle is rejected (e.g. the ASan runtime already registered a symbolizer).
# State is shared, so re-enumerate the module table via this function using the GetCurrentProcess() pseudo-handle; see traceback.vp Install.
extern def SymRefreshModuleList(hProcess: HANDLE) -> BOOL
# PVOID SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase);
@@ -130,9 +118,8 @@ extern def SymGetModuleBase64(hProcess: HANDLE, AddrBase: ULONGLONG) -> ULONGLON
# PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
# PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
# PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
# 三个回调按本仓"函数指针用 t.CPtr 传"的写法声明:实参直接写函数名(ReadProcessMemory /
# SymFunctionTableAccess64 / SymGetModuleBase64),由 TPV bitcast 成函数指针;
# TranslateAddress 传 None(0)——x64 不需要地址翻译例程。
# The three callbacks follow the "function pointers passed as t.CPtr" convention: pass the function name (ReadProcessMemory / SymFunctionTableAccess64 / SymGetModuleBase64), bitcast by TPV.
# TranslateAddress is None (0): x64 needs no address-translation routine.
extern def StackWalk64(MachineType: DWORD, hProcess: HANDLE, hThread: HANDLE,
StackFrame: STACKFRAME64*, ContextRecord: VOIDPTR,
ReadMemoryRoutine: t.CPtr, FunctionTableAccessRoutine: t.CPtr,
+2 -7
View File
@@ -63,11 +63,6 @@ extern def GlobalSize(hMem: VOIDPTR) -> sizet
extern def LocalAlloc(uFlags: UINT, dwBytes: sizet) -> VOIDPTR
extern def LocalFree(hMem: VOIDPTR) -> VOIDPTR
# 跨进程/本进程读内存(memoryapi.h / kernel32.dll)
# BOOL ReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer,
# SIZE_T nSize, SIZE_T *lpNumberOfBytesRead);
# 该导出的签名恰为 dbghelp 的 PREAD_PROCESS_MEMORY_ROUTINE64:StackWalk64 的回调槽
# 要求填这个函数(includes/traceback.vp 在 Install 时用 GetProcAddress 取其地址传入,
# 见 includes/w32/win32dbghelp.vp 的 StackWalk64)。
# 返回 0 = 失败(GetLastError 取原因)。
# Read memory (memoryapi.h / kernel32.dll): BOOL ReadProcessMemory(HANDLE, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead).
# Its signature matches dbghelp's PREAD_PROCESS_MEMORY_ROUTINE64 (the StackWalk64 callback slot); returns 0 on failure (see GetLastError).
extern def ReadProcessMemory(hProcess: HANDLE, lpBaseAddress: t.CConst | VOIDPTR, lpBuffer: VOIDPTR, nSize: sizet, lpNumberOfBytesRead: sizet*) -> BOOL
+6 -49
View File
@@ -112,39 +112,8 @@ extern def TlsGetValue(dwTlsIndex: ULONG) -> VOIDPTR
extern def TlsSetValue(dwTlsIndex: ULONG, lpTlsValue: VOIDPTR) -> BOOL
# =============================================================================
# x86-64 CONTEXT(线程上下文;winnt.h 的 DECLSPEC_ALIGN(16) CONTEXT)
#
# 用途:SuspendThread 主线程后用 GetThreadContext 取 RIP/RSP/RBP,供栈回溯。
# 字段顺序与偏移**逐字段**对齐 winnt.h 的 AMD64 定义(偏移见每行注释);字段数
# 不能裁剪——GetThreadContext(..., CONTEXT_FULL) 会把整块(含 FltSave/向量寄存器)
# 写回调用方缓冲,缓冲小于 sizeof(CONTEXT)=0x4D0=1232 即写穿堆。同理
# dbghelp!StackWalk64 每走一帧都会把它自己那份 CONTEXT(同为 0x4D0)写回传入的
# ContextRecord —— 本结构原先漏了 0x4B0/0x4B8 的 LastBranchToRip/LastBranchFromRip,
# 于是尺寸只有 0x4C0,被 ASan 抓成 "heap-buffer-overflow ... WRITE of size 1232",
# 故这两个字段**必须**保留。
#
# 布局核对(x64,非 packed,自然对齐):
# 0x000 P1Home..P6Home 6×u64
# 0x030 ContextFlags, MxCsr 2×u32
# 0x038 SegCs..SegSs 6×u16
# 0x044 EFlags 1×u32
# 0x048 Dr0..Dr3,Dr6,Dr7 6×u64
# 0x078 Rax..R15 16×u64
# 0x0F8 Rip 1×u64
# 0x100 FltSave (XMM_SAVE_AREA32) 512 字节
# 0x300 VectorRegister[26] (M128A) 416 字节
# 0x4A0 VectorControl 1×u64
# 0x4A8 DebugControl 1×u64
# 0x4B0 LastBranchToRip 1×u64
# 0x4B8 LastBranchFromRip 1×u64
# 0x4C0 LastExceptionToRip 1×u64
# 0x4C8 LastExceptionFromRip 1×u64
# 合计 0x4D0 = 1232 字节(16 的倍数;结构自身对齐要求 16,见下方调用方约定)
#
# FltSave / VectorRegister 只需占位保证偏移与总长:本模块不读其内部字段,
# 故用定长字节数组表示(起始偏移 0x100 / 0x300 本身即 16 的倍数,不引入对齐偏差)。
# 调用方约定:GetThreadContext 要求 lpContext 16 字节对齐 —— 本仓由
# stdlib.malloc/calloc 分配(x64 CRT 保证 16 字节对齐),不要用栈上局部变量。
# x86-64 CONTEXT (winnt.h DECLSPEC_ALIGN(16)); after SuspendThread, GetThreadContext reads RIP/RSP/RBP for stack walking.
# Fields/offsets must match winnt.h AMD64 exactly and must not be trimmed: GetThreadContext(CONTEXT_FULL) and StackWalk64 write sizeof(CONTEXT)=0x4D0=1232 bytes, so LastBranchToRip/LastBranchFromRip (0x4B0/0x4B8) are required. Allocate 16-byte aligned (stdlib.malloc/calloc), never a stack local.
# =============================================================================
define CONTEXT_AMD64 = 0x00100000
define CONTEXT_CONTROL = 0x00100001
@@ -202,23 +171,11 @@ class CONTEXT():
LastExceptionToRip: ULONGLONG # 0x4C0
LastExceptionFromRip: ULONGLONG # 0x4C8
# 取线程上下文(processthreadsapi.h)。hThread 需带 THREAD_GET_CONTEXT 权限;
# ContextFlags 必须先写入 CONTEXT_AMD64 | ...(x64;本仓用 CONTEXT_FULL)。
# 返回 0 = 失败(GetLastError 取原因)。
# Get thread context (processthreadsapi.h). hThread needs THREAD_GET_CONTEXT; ContextFlags must be set to CONTEXT_AMD64 | ... first (x64; CONTEXT_FULL here).
# Returns 0 on failure (see GetLastError).
extern def GetThreadContext(hThread: HANDLE, lpContext: CONTEXT*) -> BOOL
# LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER);
# 注册进程级「未处理异常」过滤器(errhandlingapi.h,kernel32)。
# 回调签名:LONG CALLBACK Filter(struct _EXCEPTION_POINTERS *)
# struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; // +0x00
# PCONTEXT ContextRecord; // +0x08 }
# struct _EXCEPTION_RECORD { DWORD ExceptionCode; // +0x00
# DWORD ExceptionFlags; // +0x04
# struct _EXCEPTION_RECORD* Nested; // +0x08
# PVOID ExceptionAddress; // +0x10
# ... } // 共 0x98 字节
# 回调在**出错线程**上运行;返回 EXCEPTION_CONTINUE_SEARCH(0) = 交还系统默认处理(不吞异常)。
# 第一个形参按本仓「函数指针用 t.CPtr 传」声明(先例同 SetConsoleCtrlHandler);返回值是
# **上一个**过滤器(首次注册时为 NULL),故本仓不检查返回值。用法见 includes/traceback.vp
# 的 Install / DumpCrashThread(崩溃栈转储)。
# SetUnhandledExceptionFilter registers a process-level unhandled-exception filter (errhandlingapi.h/kernel32); the callback runs on the faulting thread and returns EXCEPTION_CONTINUE_SEARCH(0) to use the default handler (does not swallow).
# Callback: LONG CALLBACK Filter(_EXCEPTION_POINTERS*): ExceptionRecord@0x00, ContextRecord@0x08 (EXCEPTION_RECORD=0x98). First arg declared as t.CPtr; return ignored (previous filter). See traceback.vp Install/DumpCrashThread.
extern def SetUnhandledExceptionFilter(TopLevelExceptionFilter: t.CPtr) -> t.CPtr
+4 -6
View File
@@ -39,10 +39,8 @@ extern def WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: ULONG) -> ULONG
extern def WaitForSingleObjectEx(hHandle: HANDLE, dwMilliseconds: ULONG, bAlertable: BOOL) -> ULONG
extern def WaitForMultipleObjects(nCount: ULONG, lpHandles: VOIDPTR, bWaitAll: BOOL, dwMilliseconds: ULONG) -> ULONG
extern def WaitForMultipleObjectsEx(nCount: ULONG, lpHandles: VOIDPTR, bWaitAll: BOOL, dwMilliseconds: ULONG, bAlertable: BOOL) -> ULONG
# SRWLOCK / CONDITION_VARIABLE 在 Windows 头文件里都是"单个 PVOID 的不透明结构"
# (sizeof == 8),API 收的是**存储地址**(PSRWLOCK / PCONDITION_VARIABLE)。
# 故必须建模成结构体:若声明成 VOIDPTR,`c.Addr(self.lock)` 会得到 i8**(多一层),
# 与形参 i8* 层数不符(指针隐式转换收紧后直接报 CastError)。
# SRWLOCK / CONDITION_VARIABLE are opaque single-PVOID structs (sizeof == 8) and the APIs take their storage address. Model them as structs.
# Declaring them as VOIDPTR would make c.Addr(self.lock) yield i8** (one level too many) and fail with a CastError.
class SRWLOCK:
Ptr: VOIDPTR
@@ -55,7 +53,7 @@ extern def AcquireSRWLockShared(SRWLock: SRWLOCK*) -> VOID
extern def ReleaseSRWLockExclusive(SRWLock: SRWLOCK*) -> VOID
extern def ReleaseSRWLockShared(SRWLock: SRWLOCK*) -> VOID
# 条件变量 (Condition Variables) - Vista+
# Condition variables - Vista+
define CONDITION_VARIABLE_LOCKMODE_SHARED = 0x1
extern def InitializeConditionVariable(ConditionVariable: CONDITION_VARIABLE*) -> VOID
@@ -64,7 +62,7 @@ extern def SleepConditionVariableSRW(ConditionVariable: CONDITION_VARIABLE*, SRW
extern def WakeConditionVariable(ConditionVariable: CONDITION_VARIABLE*) -> VOID
extern def WakeAllConditionVariable(ConditionVariable: CONDITION_VARIABLE*) -> VOID
# 一次性初始化 (One-Time Initialization) - Vista+
# One-time initialization - Vista+
define INIT_ONCE_STATIC_INIT = 0x00000001
define INIT_ONCE_CHECK_ONLY = 0x00000002
define INIT_ONCE_ASYNC = 0x00000004
+22 -22
View File
@@ -7,46 +7,46 @@ from w32.win32base import *
# w32/winsock2.py — WinSock2 API 声明 (ws2_32.dll)
# ============================================================
# 版本常量
# Version constants
define WINSOCK_VERSION = 0x0202# 2.2
# 地址族
# Address families
define AF_INET = 2
define AF_INET6 = 23
# 套接字类型
# Socket types
define SOCK_STREAM = 1# TCP
define SOCK_DGRAM = 2# UDP
define SOCK_RAW = 3# 原始套接字
define SOCK_RAW = 3# raw socket
# 协议
# Protocols
define IPPROTO_TCP = 6
define IPPROTO_UDP = 17
# 选项级别
define SOL_SOCKET = 0xFFFF# WinSock2 值
define SO_RCVTIMEO = 0x1006# WinSock2 值
define SO_SNDTIMEO = 0x1005# WinSock2 值
define SO_REUSEADDR = 0x0004# WinSock2 值
# Option levels
define SOL_SOCKET = 0xFFFF# WinSock2 value
define SO_RCVTIMEO = 0x1006# WinSock2 value
define SO_SNDTIMEO = 0x1005# WinSock2 value
define SO_REUSEADDR = 0x0004# WinSock2 value
# 特殊值
# Special values
define INADDR_ANY = 0
define SOCKET_ERROR = -1
define INVALID_SOCKET = 0xFFFFFFFF# WinSock2: ~0
# 发送/接收标志
define MSG_NOSIGNAL = 0# Windows 不支持,设为 0
# Send/receive flags
define MSG_NOSIGNAL = 0# Unsupported on Windows, set to 0
# 关闭方式
# Shutdown modes
define SD_SEND = 1
define SD_RECV = 0
define SD_BOTH = 2
# ============================================================
# 结构体
# Structures
# ============================================================
# struct sockaddr_in — 16 字节
# struct sockaddr_in -- 16 bytes
class WSASocketAddr:
family: u16 # sin_family
port: u16 # sin_port
@@ -54,7 +54,7 @@ class WSASocketAddr:
zero: u64 # sin_zero
# struct WSADATA — 400 字节
# struct WSADATA -- 400 bytes
class WSAData:
wVersion: WORD # @ 0
wHighVersion: WORD # @ 2
@@ -62,10 +62,10 @@ class WSAData:
szSystemStatus: BYTE # @ 261 (129 bytes + padding)
iMaxSockets: u16 # @ 390
iMaxUdpDg: u16 # @ 392
lpVendorInfo: CHARPTR # @ 394 (实际上指针是 8 字节对齐)
lpVendorInfo: CHARPTR # @ 394 (pointer is 8-byte aligned)
# struct hostent — 自然对齐: h_name(8) + h_aliases(8) + h_addrtype(2) + h_length(2) + auto-pad(4) + h_addr_list(8) = 32
# struct hostent -- natural alignment: h_name(8) + h_aliases(8) + h_addrtype(2) + h_length(2) + auto-pad(4) + h_addr_list(8) = 32
class WSAHostEnt:
h_name: CHARPTR
h_aliases: CHARPTR
@@ -76,12 +76,12 @@ class WSAHostEnt:
# struct timeval
class WinTimeVal:
tv_sec: LONG # 秒
tv_usec: LONG # 微秒
tv_sec: LONG # seconds
tv_usec: LONG # microseconds
# ============================================================
# WinSock2 API 函数
# WinSock2 API functions
# ============================================================
extern def WSAStartup(wVersionRequested: WORD, lpWSAData: WSAData*) -> INT
+18 -19
View File
@@ -204,19 +204,18 @@ def zf_read_whole(path: str, out_len: sizet*) -> uint8*:
# ============================================================
# 条目信息(目录项)
# 注意: 节点为显式 calloc 分配
# 由 ZipFile 负责释放——避免 GC 实例经 Ptr 字段引用被回收。
# Entry info (central directory item). Nodes are explicit calloc allocations, owned and
# freed by ZipFile (avoids GC reclaiming instances referenced via Ptr fields).
# ============================================================
class ZipInfo:
filename: bytes # 原始字节名(含 \0,zipfile 持有并负责释放)
filename: bytes # raw byte name (incl \0; owned and freed by zipfile)
name_len: sizet
compress_type: int # ZIP_STORED / ZIP_DEFLATED
crc32: uint32
compress_size: sizet
file_size: sizet
header_offset: sizet # 本地文件头偏移
next: ZipInfo* # 链表(保持写入/目录顺序)
header_offset: sizet # local file header offset
next: ZipInfo* # linked list (keeps write/directory order)
# ============================================================
@@ -224,22 +223,22 @@ class ZipInfo:
# ============================================================
class ZipFile:
path: bytes
mode: int = -1 # 0 = 读,1 = 写
compression: int # write() 未显式指定时的压缩类型
fh: wb.HANDLE = None # 写模式句柄(裸 win32 CreateFileA)
data: bytes = None # 读模式:整个归档的内存副本
mode: int = -1 # 0 = read, 1 = write
compression: int # compression used when write() does not specify one
fh: wb.HANDLE = None # write-mode handle (raw win32 CreateFileA)
data: bytes = None # read mode: in-memory copy of whole archive
data_len: sizet = 0
info_head: ZipInfo* = None
info_tail: ZipInfo* = None
info_count: sizet = 0
pos: sizet = 0 # 写模式当前偏移
ok: int = 0 # 1 = 可正常使用
pos: sizet = 0 # write-mode current offset
ok: int = 0 # 1 = usable
closed: int = 0
def __init__(self, path: str, mode: str = "r", compression: int = ZIP_DEFLATED):
self.path = path
self.compression = compression
# 注意: 字符串必须用 strcmp 比较(== 只会比较指针)
# Note: strings must be compared with strcmp (== compares pointers only).
if string.strcmp(mode, "r") == 0: self.mode = 0
elif string.strcmp(mode, "w") == 0: self.mode = 1
else:
@@ -298,14 +297,14 @@ class ZipFile:
self.info_count += 1
return node
# ---- 读模式:解析中央目录 ----
# ---- Read mode: parse central directory ----
def _parse(self) -> int:
if self.data_len < 22:
zipfile_set_error(ZIP_ERR_NOT_ZIP, "file too small to be a zip")
return ZIP_ERR_NOT_ZIP
d: bytes = self.data
# 从尾部回扫 EOCD(容忍尾部注释)
# Scan back from tail for EOCD (tolerates trailing comment)
max_back: sizet = self.data_len - 22
if max_back > _ZIP_MAX_COMMENT + 20:
max_back = _ZIP_MAX_COMMENT + 20
@@ -371,7 +370,7 @@ class ZipFile:
cp = cp + rec_len
return ZIP_ERR_OK
# ---- 公共查询 ----
# ---- Public queries ----
def namelist_at(self, i: sizet) -> bytes:
if self.ok == 0 or self.info_count == 0 or i >= self.info_count:
return None
@@ -394,7 +393,7 @@ class ZipFile:
n = n.next
return None
# ---- 读成员:解压 + CRC32 校验 ----
# ---- Read member: decompress + CRC32 check ----
def read(self, name: str, out_len: sizet*) -> bytes:
if not out_len:
zipfile_set_error(ZIP_ERR_INVALID, "out_len is None")
@@ -463,7 +462,7 @@ class ZipFile:
return None
return out
# ---- 写成员 ----
# ---- Write member ----
def write(self, arcname: str, data: bytes, data_len: sizet,
compress_type: int = -1) -> int:
if self.closed or self.ok == 0:
@@ -549,7 +548,7 @@ class ZipFile:
node.header_offset = hoff
return ZIP_ERR_OK
# ---- 写模式收尾:中央目录 + EOCD ----
# ---- Write-mode finish: central directory + EOCD ----
def _finish_write(self) -> int:
if self.info_count > _ZIP_MAX_COMMENT:
zipfile_set_error(ZIP_ERR_UNSUPPORTED, "too many entries (>65535)")
+4 -7
View File
@@ -140,7 +140,7 @@ class Compress:
if not out_len:
set_error(Z_STREAM_ERROR, "out_len is None")
return None
s: rawptr[zdeflate.zdeflate_stream] = self.stream # 隐式类型转换
s: rawptr[zdeflate.zdeflate_stream] = self.stream # implicit type conversion
if mode == Z_FINISH:
if not self.header_written:
self.header_written = 1
@@ -231,7 +231,7 @@ class Compress:
set_error(Z_MEM_ERROR, "out of memory")
return None
copy_obj.pool = self.pool
s: rawptr[zdeflate.zdeflate_stream] = self.stream # 隐式类型转换
s: rawptr[zdeflate.zdeflate_stream] = self.stream # implicit type conversion
copy_obj.stream = s.copy()
if not copy_obj.stream:
if self.pool == None:
@@ -274,7 +274,7 @@ class Compress:
def delete(self):
if self.is_initialized and self.stream:
s: rawptr[zdeflate.zdeflate_stream] = self.stream # 隐式类型转换
s: rawptr[zdeflate.zdeflate_stream] = self.stream # implicit type conversion
s.destroy()
if self.pool == None:
stdlib.free(self.zdict)
@@ -482,10 +482,7 @@ def clone_bytes(pool: rawptr[memhub.MemManager], src: BYTEPTR, length: sizet) ->
if dst: string.memcpy(dst, src, length)
return dst
# buf 是"指针的指针"(i8**):不要写成 `BYTE | t.CPtr[t.CPtr]`——联合里嵌
# `t.CPtr[...]` 下标时声明侧(StubGen/VpiReader)不认作 CPtr 标记,会坍缩成
# 首段 BYTE = i8,与定义侧 i8** 不一致(跨模块调用即 ABI 错位,见
# includes/zlib/zinflate.vp decompress 处说明)。改用"指针别名 | t.CPtr"。
# buf is a "pointer to pointer" (i8**). Do NOT write `BYTE | t.CPtr[t.CPtr]`: with an embedded `t.CPtr[...]` subscript the declare side (StubGen/VpiReader) does not recognize it as a CPtr marker and collapses it to BYTE = i8, mismatching the definition side's i8** (cross-module ABI mismatch; see includes/zlib/zinflate.vp decompress). Use "pointer alias | t.CPtr" instead.
def append_bytes(pool: rawptr[memhub.MemManager], buf: BYTEPTR*, length: sizet*, cap: sizet*,
data: BYTEPTR, data_len: sizet) -> int:
if not data or data_len == 0: return 0
+5 -10
View File
@@ -52,9 +52,7 @@ class zdeflate_stream:
return best_len if (best_len >= zdef.ZDEFLATE_MIN_MATCH) else 0
def update_hash(self, data: BYTE*, pos: sizet, data_len: sizet):
# 只在还能作为匹配起点的位置维护 hash(pos+2 >= data_len 的尾部
# 字节不会再被 find_match 引用,且 zdeflate_hash 会读 pos..pos+2,
# 越过末尾则越界——见 ASan heap-buffer-overflow)
# Only maintain the hash at positions that can still start a match: tail bytes with pos+2 >= data_len are never referenced by find_match, and zdeflate_hash reads pos..pos+2, so going past the end overflows (see ASan heap-buffer-overflow).
if pos + 2 >= data_len: return
h: uint = zdeflate_hash(data + pos)
self.hash_prev[pos & zdef.ZDEFLATE_WINDOW_MASK] = self.hash_head[h]
@@ -165,8 +163,7 @@ class zdeflate_stream:
def write_stored_block(self, data: UINT8PTR, length: sizet, final: int):
self.writer.stored_block(data, length, final)
# out 是"指针的指针"(i8**):见 zinflate.vp decompress 处的说明——
# 联合里嵌 `t.CPtr[...]` 会让声明侧(VpiReader)坍缩成 i8,与定义侧 i8** 不一致。
# out is a "pointer to pointer" (i8**): see zinflate.vp decompress -- an embedded `t.CPtr[...]` collapses the declare side (VpiReader) to i8, mismatching the definition side's i8**.
def compress(self, data: UINT8PTR, length: sizet,
out: BYTEPTR*, out_len: sizet*) -> int:
if self.is_finished: return -2
@@ -184,7 +181,7 @@ class zdeflate_stream:
string.memcpy(*out, self.writer.buf, *out_len)
return 0
# out 同 compress:i8**(声明侧/定义侧必须一致)。
# out same as compress: i8** (declare side and definition side must match).
def flush(self, mode: int, out: BYTEPTR*, out_len: sizet*) -> int:
if mode == 4:
if not self.is_finished:
@@ -335,8 +332,7 @@ def zdeflate_count_cl_freqs(all_lengths: INTPTR, total: int, cl_freqs: INTPTR):
cl_freqs[16] += 1
run -= count
i += count
# 剩余的 1~2 个相同长度值不静默跳过,交由外层循环
# 按字面量(符号 val)逐个统计,保证总覆盖数恰为 total
# Remaining 1~2 equal-length values are not silently skipped; the outer loop counts them literally (symbol val), so the covered total is exactly total
def zdeflate_write_cl_encoded(all_lengths: INTPTR, total: int,
w: rawptr[zdef.zbit_writer], cl_tree: rawptr[zhuff.zhuff_tree]):
@@ -375,8 +371,7 @@ def zdeflate_write_cl_encoded(all_lengths: INTPTR, total: int,
w.write_bits(count - 3, 2)
run -= count
i += count
# 剩余的 1~2 个相同长度值不静默跳过,交由外层循环
# 按字面量(符号 val)逐个编码,保证 RLE 覆盖总数恰为 total
# Remaining 1~2 equal-length values are not silently skipped; the outer loop encodes them literally (symbol val), so RLE coverage totals exactly total
+5 -9
View File
@@ -69,7 +69,7 @@ class zinflate_stream:
self.is_gzip = 1
if self.input_pos + 10 > self.input_len: return -3
self.input_pos += 10
# FLG 位于 gzip 头第 4 字节(偏移 3),即 self.input_pos - 7
# FLG is at byte 4 of the gzip header (offset 3), i.e. self.input_pos - 7
flg: BYTE = self.input_data[self.input_pos - 7]
if flg & 0x04:
if self.input_pos + 2 > self.input_len: return -3
@@ -375,14 +375,10 @@ class zinflate_stream:
self.input_pos += 4
return 0
# out 是"指针的指针"(i8**)。不要写成 `t.CUInt8T | t.CPtr[t.CPtr]`:
# 联合里嵌 `t.CPtr[...]` 下标时,定义侧按 PtrCount 累加得 i8**,而 stub/declare
# 侧(StubGen/VpiReader)只把整段等于 `t.CPtr`/`CPtr` 的段认作 CPtr 标记,
# 于是坍缩成首段 `t.CUInt8T` = i8 → 跨模块 ABI 不一致(调用方只传 1 字节,
# 被调用方按 8 字节指针写入 → ASan heap-buffer-overflow)。
# 改用"指针别名 | t.CPtr":两侧都按"追加 1 层指针"解析,同为 i8**。
# 同形先例:includes/w32/win32sync.vp:65 `Context: VOIDPTR | t.CPtr`、
# includes/posix.vp:199 `argv: CHARPTR | t.CPtr`。
# out is a "pointer to pointer" (i8**). Do NOT write `t.CUInt8T | t.CPtr[t.CPtr]`: with an embedded `t.CPtr[...]` subscript the definition side accumulates PtrCount to i8**, but the stub/declare side (StubGen/VpiReader) only recognizes a segment exactly equal to `t.CPtr`/`CPtr` as the CPtr marker, collapsing it to t.CUInt8T = i8 -> cross-module ABI mismatch (caller passes 1 byte, callee writes an 8-byte pointer -> ASan heap-buffer-overflow).
# Use "pointer alias | t.CPtr" instead: both sides append one pointer level and resolve to i8**.
# Same-shaped precedents: includes/w32/win32sync.vp:65 `Context: VOIDPTR | t.CPtr`,
# includes/posix.vp:199 `argv: CHARPTR | t.CPtr`.
def decompress(self, data: UINT8PTR, length: sizet,
max_length: sizet, out: BYTEPTR*, out_len: sizet*) -> int:
if not self.is_initialized: return -2