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:
1 parent
27121f56a7
commit
e71daeea0e
75 files changed
+3040
-4214
No files matched your search
@@ -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__
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
+8
-10
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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])
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
+557
-590
File diff suppressed because it is too large.
Load diff
+59
-76
@@ -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
File diff suppressed because it is too large.
Load diff
+201
-351
File diff suppressed because it is too large.
Load diff
+43
-67
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user