WIP 快照:includes 现有改动(含 M2.4 new 描述符的语法支持)

语法侧(M2.4):ast/base.vp 枚举尾部追加 ASTKind.New;ast/exprs.vp 新增 NEW_SRC_*/NEW_LIFE_* 与 class New;ast/parser.vp 新增 _is_new_expr/_parse_new_expr(new 为软关键字,不进 Keyword 表,new=1/new.x/new(1) 仍是标识符);ast/__init__.vp 再导出 New 与常量。

其余为既有 WIP 改动一并快照,保持基线一致(39 文件,+312/-237)。

未纳入:ctraits.vpi(已删除)、ctraits.vpnci、tomllib/(均未跟踪)。
This commit is contained in:
TermiNexus committed 2026-10-08 17:45:31 +08:00
1 parent e3cdb190dc
commit e25e8958f0
39 files changed
+312 -237

No files matched your search

+4 -5
View File
@@ -1,4 +1,3 @@
import t, c
import memhub import memhub
import string import string
import json import json
@@ -28,7 +27,7 @@ class dict:
mb: memhub.MemBuddy = memhub.MemBuddy(arena, arena_size) mb: memhub.MemBuddy = memhub.MemBuddy(arena, arena_size)
d: dict = dict(mb) d: dict = dict(mb)
d.set_int("x", 42) d.set_int("x", 42)
p: t.CPtr = d["x"] p: rawptr = d["x"]
v: int = c.Deref((int*)(p)) # int value stored as i32; read it back with an int* view v: int = c.Deref((int*)(p)) # int value stored as i32; read it back with an int* view
# Nested dict # Nested dict
@@ -82,7 +81,7 @@ class dict:
Use typed methods such as set_int / set_str for persistent storage.""" Use typed methods such as set_int / set_str for persistent storage."""
self._set_variant_raw(key, VARIANT_PTR, val) self._set_variant_raw(key, VARIANT_PTR, val)
def _set_variant_raw(self, key: str, vtype: int, ptr: t.CPtr): def _set_variant_raw(self, key: str, vtype: int, ptr: rawptr):
"""Internal method: store Variant entry (vtype + ptr). No automatic address resolution.""" """Internal method: store Variant entry (vtype + ptr). No automatic address resolution."""
idx: sizet = self._find(key) idx: sizet = self._find(key)
if idx < self.__count__: if idx < self.__count__:
@@ -143,7 +142,7 @@ class dict:
"""Store nested dictionary pointer (VARIANT_DICT).""" """Store nested dictionary pointer (VARIANT_DICT)."""
self._set_variant_raw(key, VARIANT_DICT, val) self._set_variant_raw(key, VARIANT_DICT, val)
def set_list(self, key: str, val: t.CPtr): def set_list(self, key: str, val: rawptr):
"""Store nested list pointer (VARIANT_LIST).""" """Store nested list pointer (VARIANT_LIST)."""
self._set_variant_raw(key, VARIANT_LIST, val) self._set_variant_raw(key, VARIANT_LIST, val)
@@ -166,7 +165,7 @@ class dict:
vtype_ptr: int* = int*(self.__values__ + idx * _VARIANT_SIZE) vtype_ptr: int* = int*(self.__values__ + idx * _VARIANT_SIZE)
return vtype_ptr[0] return vtype_ptr[0]
def get(self, key: str, default: t.CPtr) -> t.CPtr: def get(self, key: str, default: rawptr) -> rawptr:
"""Return value pointer; return default pointer if key does not exist.""" """Return value pointer; return default pointer if key does not exist."""
idx: sizet = self._find(key) idx: sizet = self._find(key)
if idx >= self.__count__: if idx >= self.__count__:
-3
View File
@@ -1,6 +1,3 @@
import t
# Variant type tags (fixed enumeration + custom pointer type) # Variant type tags (fixed enumeration + custom pointer type)
define VARIANT_NONE = 0 # Untyped / null value define VARIANT_NONE = 0 # Untyped / null value
define VARIANT_INT = 1 # int (i64) define VARIANT_INT = 1 # int (i64)
+6 -6
View File
@@ -1,4 +1,4 @@
import t, c import t
import string import string
import memhub import memhub
import stdio import stdio
@@ -90,14 +90,14 @@ class ParsedArgs:
if self._ht.__contains__(name) == 0: if self._ht.__contains__(name) == 0:
return 0 return 0
ptr: t.CPtr = self._ht[name] ptr: t.CPtr = self._ht[name]
return c.Deref(int(uint64(ptr), t.CPtr)) return *(int*(ptr))
def get_bool(self, name: str) -> bool: def get_bool(self, name: str) -> bool:
"""Fetch boolean value. Returns false if key not found.""" """Fetch boolean value. Returns false if key not found."""
if self._ht.__contains__(name) == 0: if self._ht.__contains__(name) == 0:
return False return False
ptr: t.CPtr = self._ht[name] ptr: rawptr = self._ht[name]
return c.Deref(int(uint64(ptr), t.CPtr)) != 0 return *(int*(ptr)) != 0
def has(self, name: str) -> bool: def has(self, name: str) -> bool:
"""Check whether specified key exists.""" """Check whether specified key exists."""
@@ -128,11 +128,11 @@ class ArgumentParser:
__mbuddy__: memhub.MemBuddy* __mbuddy__: memhub.MemBuddy*
def __new__(self, prog: str = "", description: str = "", def __new__(self, prog: str = "", description: str = "",
pool: memhub.MemBuddy* = None) -> t.CPtr: pool: memhub.MemBuddy* = None) -> rawptr:
mb: memhub.MemBuddy* = pool mb: memhub.MemBuddy* = pool
if mb is None: if mb is None:
mb = _mbuddy mb = _mbuddy
ret: t.CPtr = mb.alloc(40) ret: rawptr = mb.alloc(40)
return ret return ret
def __init__(self, prog: str = "", description: str = "", def __init__(self, prog: str = "", description: str = "",
+5 -2
View File
@@ -1,7 +1,6 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string
# Import submodule # Import submodule
from .tokens import ( from .tokens import (
@@ -37,6 +36,10 @@ from .exprs import (
Constant, NamedExpr, Attribute, Subscript, Starred, Name, Constant, NamedExpr, Attribute, Subscript, Starred, Name,
List, Tuple, Slice, List, Tuple, Slice,
Call, Compare, OpNode, Call, Compare, OpNode,
# M2.4 `new` 描述符分配节点与描述符常量
New,
NEW_SRC_DEFAULT, NEW_SRC_STACK, NEW_SRC_HEAP, NEW_SRC_MEMGR,
NEW_LIFE_DEFAULT, NEW_LIFE_SLIFE, NEW_LIFE_HLIFE,
name_get_id, constant_get_str_val, name_get_id, constant_get_str_val,
binop_get_left, binop_get_right, binop_get_left, binop_get_right,
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
+2 -1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
@@ -44,6 +43,8 @@ enum ASTKind:
MatchClass, MatchStar, MatchAs, MatchOr MatchClass, MatchStar, MatchAs, MatchOr
# Soft keyword statement (appended last to keep existing enum values unchanged) # Soft keyword statement (appended last to keep existing enum values unchanged)
FallThrough FallThrough
# `new` 描述符分配表达式(M2.4;同样只在枚举尾部追加,既有取值不变)
New
# ============================================================ # ============================================================
+69 -2
View File
@@ -1,8 +1,6 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
import stdio
from .base import ( from .base import (
AST, ExprNode, ASTKind, ASTCtx, OpKind, AST, ExprNode, ASTKind, ASTCtx, OpKind,
_init_ast, _copy_str, _set_parent_list, _init_ast, _copy_str, _set_parent_list,
@@ -326,6 +324,75 @@ class Call(ExprNode):
return pos return pos
# ============================================================
# New Node(M2.4:`new` 描述符分配)
# ============================================================
# 两条正交轴,各取其一;顺序无关;重复/冲突在解析期即报错。
# 来源 SourceKind:缺省 = heap(D3)
define NEW_SRC_DEFAULT = 0
define NEW_SRC_STACK = 1
define NEW_SRC_HEAP = 2
define NEW_SRC_MEMGR = 3
# 生命期 LifeKind:缺省由来源决定(stack→slife,heap/memgr→hlife)
define NEW_LIFE_DEFAULT = 0
define NEW_LIFE_SLIFE = 1
define NEW_LIFE_HLIFE = 2
class New(ExprNode):
"""New(Source: CInt, Life: CInt, memgr: AST | CPtr, func: AST, args: list[AST | CPtr])"""
Source: int # SourceKind(NEW_SRC_*)
Life: int # LifeKind(NEW_LIFE_*)
memgr: AST* # `memgr(x)` 的 x;无 memgr 描述符时为 None
func: AST* # 被构造的类型/可调用(`new ... T(args)` 里的 T)
args: list[AST*]*
def __new__(self, pool: memhub.MemManager*,
Source: int,
Life: int,
memgr: AST*,
func: AST*,
args: list[AST*]*):
ptr: New* = pool.alloc(New.__sizeof__())
if ptr: string.memset(ptr, 0, New.__sizeof__())
return ptr
def __init__(self, pool: memhub.MemManager*,
Source: int,
Life: int,
memgr: AST*,
func: AST*,
args: list[AST*]*):
_init_ast(self, pool, 0, 0)
self.Source = Source
self.Life = Life
self.memgr = memgr
self.func = func
self.args = args
_set_parent_list(args, self)
def kind(self) -> int:
return ASTKind.New
def type_name(self) -> str:
return "New"
def dump(self, buf: str, size: sizet, pos: sizet) -> sizet:
pos = _emit(buf, size, pos, "New(")
if self.memgr is not None:
pos = _emit(buf, size, pos, ", memgr=")
pos = self.memgr.dump(buf, size, pos)
pos = _emit(buf, size, pos, ", func=")
if self.func is not None:
pos = self.func.dump(buf, size, pos)
else:
pos = _emit(buf, size, pos, "None")
pos = _emit(buf, size, pos, ", args=")
pos = _dump_list(self.args, buf, size, pos)
pos = _emit(buf, size, pos, ")")
return pos
# ============================================================ # ============================================================
# BoolOp Node # BoolOp Node
# ============================================================ # ============================================================
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
+114 -27
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
@@ -17,7 +16,9 @@ from .exprs import (
ListComp, SetComp, DictComp, GeneratorExp, ListComp, SetComp, DictComp, GeneratorExp,
Await, Yield, YieldFrom, FormattedValue, JoinedStr, Await, Yield, YieldFrom, FormattedValue, JoinedStr,
Constant, NamedExpr, Attribute, Subscript, Starred, Name, Constant, NamedExpr, Attribute, Subscript, Starred, Name,
List, Tuple, Slice, Call, Compare, OpNode, List, Tuple, Slice, Call, Compare, OpNode, New,
NEW_SRC_DEFAULT, NEW_SRC_STACK, NEW_SRC_HEAP, NEW_SRC_MEMGR,
NEW_LIFE_DEFAULT, NEW_LIFE_SLIFE, NEW_LIFE_HLIFE,
) )
from .stmts import ( from .stmts import (
Module, Expression, Interactive, FunctionType, Module, Expression, Interactive, FunctionType,
@@ -1780,6 +1781,90 @@ def _parse_slice(ps: Parser*) -> AST*:
return lower return lower
def _is_new_expr(ps: Parser*) -> int:
"""是否 `new` 描述符表达式(软关键字判定)。
`new` 是软关键字:只有紧跟一个**普通名**(描述符 hlife/slife/stack/heap/memgr 或类型名
`T(args)`)时才当关键字。`new = 1`、`new.x`、`new(1)`、`for new in ...` 都仍是标识符。
"""
if _is_named_soft(ps, "new") == 0: return 0
nxt: Token* = ps.cur.next
if nxt is None: return 0
if nxt.type != TokenType.Name: return 0
if nxt.kw_subtype != Keyword.NotKeyword: return 0
return 1
def _parse_new_expr(ps: Parser*) -> AST*:
"""`new [Descriptor...] T(args)`:描述符顺序无关、各轴最多一次(冲突即报错)。
结果恒为 `T*`;来源缺省 = heap(D3),生命期缺省由来源决定(语义侧落地)。
"""
lineno: int = _cur_lineno(ps)
col: int = _cur_col(ps)
_advance(ps) # 吃掉 `new`
src: int = NEW_SRC_DEFAULT
life: int = NEW_LIFE_DEFAULT
memgr_arg: AST* = None
while True:
if _is_named_soft(ps, "hlife") != 0:
if life != NEW_LIFE_DEFAULT:
_record_parse_err(ps)
ps.error_count += 1
return None
life = NEW_LIFE_HLIFE
_advance(ps)
continue
if _is_named_soft(ps, "slife") != 0:
if life != NEW_LIFE_DEFAULT:
_record_parse_err(ps)
ps.error_count += 1
return None
life = NEW_LIFE_SLIFE
_advance(ps)
continue
if _is_named_soft(ps, "stack") != 0:
if src != NEW_SRC_DEFAULT:
_record_parse_err(ps)
ps.error_count += 1
return None
src = NEW_SRC_STACK
_advance(ps)
continue
if _is_named_soft(ps, "heap") != 0:
if src != NEW_SRC_DEFAULT:
_record_parse_err(ps)
ps.error_count += 1
return None
src = NEW_SRC_HEAP
_advance(ps)
continue
if _is_named_soft(ps, "memgr") != 0:
if src != NEW_SRC_DEFAULT:
_record_parse_err(ps)
ps.error_count += 1
return None
_advance(ps) # memgr
if _expect_op(ps, TokOp.LPar) == 0: return None
_skip_nl(ps)
memgr_arg = _parse_test(ps)
if memgr_arg is None: return None
if _expect_op(ps, TokOp.RPar) == 0: return None
src = NEW_SRC_MEMGR
continue
break
# 类型/可调用 + 实参:复用普通表达式解析,要求结果恰好是一个 Call(`T(args)`)
callee: AST* = _parse_atom_expr(ps)
if callee is None or callee.kind() != ASTKind.Call:
_record_parse_err(ps)
ps.error_count += 1
return None
cl: Call* = Call*(callee)
node: New* = New(ps.pool, src, life, memgr_arg, cl.func, cl.args)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_atom(ps: Parser*) -> AST*: def _parse_atom(ps: Parser*) -> AST*:
"""atom: the smallest unit of expression""" """atom: the smallest unit of expression"""
ttype: int = _cur_type(ps) ttype: int = _cur_type(ps)
@@ -1787,6 +1872,8 @@ def _parse_atom(ps: Parser*) -> AST*:
col: int = _cur_col(ps) col: int = _cur_col(ps)
if ttype == TokenType.Name: if ttype == TokenType.Name:
# M2.4 `new` 描述符分配(软关键字):仅当后面确实跟着描述符/类型调用时才当关键字
if _is_new_expr(ps) != 0: return _parse_new_expr(ps)
kw: int = _cur_kw(ps) kw: int = _cur_kw(ps)
if kw == Keyword.True_: if kw == Keyword.True_:
_advance(ps) _advance(ps)
@@ -2449,31 +2536,31 @@ def _parse_array_prefix_declarator(ps: Parser*) -> AST*:
return carray return carray
def _IsCTypedefAnnotation(ann: AST*) -> int: def _IsCTypedefAnnotation(ann: AST*) -> int:
"""1 if the annotation is a typedef marker (bare CTypedef / t.CTypedef / a union containing it). """1 if the annotation is a typedef marker (bare CTypedef / t.CTypedef / a union containing it).
Used to parse a typedef's right-hand side as a **type**: `X: t.CTypedef = const uint16*` Used to parse a typedef's right-hand side as a **type**: `X: t.CTypedef = const uint16*`
synthesizes descriptors and the `*` suffix through _parse_type_ann (the annotation rule), synthesizes descriptors and the `*` suffix through _parse_type_ann (the annotation rule),
so both sides agree. TPC's vast Parser._IsCTypedefAnnotation uses the same name-based test. so both sides agree. TPC's vast Parser._IsCTypedefAnnotation uses the same name-based test.
""" """
if ann is None: return 0 if ann is None: return 0
kd: int = ann.kind() kd: int = ann.kind()
if kd == ASTKind.Attribute: if kd == ASTKind.Attribute:
at: Attribute* = Attribute*(ann) at: Attribute* = Attribute*(ann)
if at.attr is not None and string.strcmp(at.attr, "CTypedef") == 0: return 1 if at.attr is not None and string.strcmp(at.attr, "CTypedef") == 0: return 1
return 0 return 0
if kd == ASTKind.Name: if kd == ASTKind.Name:
nm: Name* = Name*(ann) nm: Name* = Name*(ann)
if nm.id is not None and string.strcmp(nm.id, "CTypedef") == 0: return 1 if nm.id is not None and string.strcmp(nm.id, "CTypedef") == 0: return 1
return 0 return 0
if kd == ASTKind.BinOp: if kd == ASTKind.BinOp:
bo: BinOp* = BinOp*(ann) bo: BinOp* = BinOp*(ann)
if bo.left is not None and _IsCTypedefAnnotation(bo.left) != 0: return 1 if bo.left is not None and _IsCTypedefAnnotation(bo.left) != 0: return 1
if bo.right is not None and _IsCTypedefAnnotation(bo.right) != 0: return 1 if bo.right is not None and _IsCTypedefAnnotation(bo.right) != 0: return 1
return 0 return 0
return 0 return 0
def _parse_type_ann(ps: Parser*) -> AST*: def _parse_type_ann(ps: Parser*) -> AST*:
"""Parse a type annotation: descriptor prefixes (const / volatile / __attribute__) and C-style pointer """Parse a type annotation: descriptor prefixes (const / volatile / __attribute__) and C-style pointer
suffixes (* / **), field-identical to TPC's expansion. `static` is invalid here; missing type / non-type suffixes (* / **), field-identical to TPC's expansion. `static` is invalid here; missing type / non-type
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
+3 -6
View File
@@ -1,6 +1,3 @@
import t, c
define ATOMIC_RELAXED = 0 define ATOMIC_RELAXED = 0
define ATOMIC_CONSUME = 1 define ATOMIC_CONSUME = 1
define ATOMIC_ACQUIRE = 2 define ATOMIC_ACQUIRE = 2
@@ -11,8 +8,8 @@ define ATOMIC_SEQ_CST = 5
def __atomic_test_and_set(ptr: uint64*, order: int) -> bool: def __atomic_test_and_set(ptr: uint64*, order: int) -> bool:
result: uint8 = 1 result: uint8 = 1
c.Asm(f"""mov al, 1 __asm__(f"""mov al, 1
xchg byte ptr [{c.AsmInp(ptr, t.ASM_DESCR.REG_ANY)}], al xchg byte ptr [{asminp(ptr, ASM_DESCR.REG_ANY)}], al
mov $0, al""", mov $0, al""",
out = [asmout(result, ASM_DESCR.OUTPUT_REG)], out = [asmout(result, ASM_DESCR.OUTPUT_REG)],
op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX]) op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
@@ -20,7 +17,7 @@ def __atomic_test_and_set(ptr: uint64*, order: int) -> bool:
def __atomic_clear(ptr: uint64*, order: int): def __atomic_clear(ptr: uint64*, order: int):
__asm__(f"""mov byte ptr [{c.AsmInp(ptr, t.ASM_DESCR.REG_ANY)}], 0""", __asm__(f"""mov byte ptr [{asminp(ptr, ASM_DESCR.REG_ANY)}], 0""",
inp = [asminp(ptr, ASM_DESCR.REG_ANY)], inp = [asminp(ptr, ASM_DESCR.REG_ANY)],
op = [ASM_DESCR.CLOBBER_MEMORY]) op = [ASM_DESCR.CLOBBER_MEMORY])
-3
View File
@@ -1,6 +1,3 @@
import t, c
# ============================================== # ==============================================
# Base64 Encoder & Decoder, pure bare-metal implementation # Base64 Encoder & Decoder, pure bare-metal implementation
# ============================================== # ==============================================
+1 -1
View File
@@ -1,4 +1,4 @@
import t, c import t
import stdlib import stdlib
-1
View File
@@ -1,4 +1,3 @@
import stdint as __stdint
import t import t
+1 -2
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
from w32.win32base import * from w32.win32base import *
from w32.win32sync import ( from w32.win32sync import (
@@ -42,7 +41,7 @@ class Condition:
def release(self): def release(self):
LeaveCriticalSection(&self.cs) LeaveCriticalSection(&self.cs)
def __enter__(self) -> 'Condition' | t.CPtr: def __enter__(self) -> Condition*:
self.acquire() self.acquire()
return self return self
+1 -6
View File
@@ -1,8 +1,3 @@
import t, c
from stdint import *
import stdlib
novirtual class Coroutine: novirtual class Coroutine:
"""Coroutine frame handle (M1: __ptr__ holds the frame pointer returned by create_<name>()). """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.""" Pure handle semantics: never instantiated or polymorphically dispatched, the compiler intercepts all coro.* calls; user code uses t.CPtr as the handle."""
@@ -43,7 +38,7 @@ def coro_gather(a: Coroutine) -> int:
# M4 placeholder: the compiler inlines coro.coro_gather(frames...) into a concurrent-advance loop. # M4 placeholder: the compiler inlines coro.coro_gather(frames...) into a concurrent-advance loop.
return 0 return 0
def coro_exception_msg(c: Coroutine) -> t.CPtr: def coro_exception_msg(c: Coroutine) -> rawptr:
# M7 placeholder: the compiler inlines coro.coro_exception_msg(frame) into a frame msg-slot read. # 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). # Note: TPV (Viper) does not support bare null; use t.CPtr(0) to build a null pointer (compatible with both compilers).
return None return None
-20
View File
@@ -1,20 +0,0 @@
"""ctraits — Compile-time type trait library
Provides compile-time type predicates with no runtime code generated.
All functions are evaluated exclusively at compile time and can be combined with the conditional
compilation statements cif / celif / celse.
"""
import t
def isptr(x: t.CType) -> bool:
"""Compile-time predicate to determine whether the type of x is a pointer.
Returns True if x is a pointer type (ptr_count > 0); otherwise returns False.
Can be used alongside cif to implement conditional compilation.
Example:
cif ctraits.isptr(my_var):
# This branch compiles only when my_var is a pointer type
...
"""
pass
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
from w32.win32base import * from w32.win32base import *
from w32.win32sync import CreateEventA, SetEvent, ResetEvent, WaitForSingleObject from w32.win32sync import CreateEventA, SetEvent, ResetEvent, WaitForSingleObject
+5 -6
View File
@@ -14,7 +14,6 @@
# #
# Covers the three deflate block types (stored/fixed/dynamic); does not decode CRC. # Covers the three deflate block types (stored/fixed/dynamic); does not decode CRC.
# ============================================================ # ============================================================
import t, c
import stdlib import stdlib
import string import string
import w32.fileio as fileio import w32.fileio as fileio
@@ -132,11 +131,11 @@ class GzipFile:
_g_bits: uint64 = 0 _g_bits: uint64 = 0
_g_bitcnt: int = 0 _g_bitcnt: int = 0
_g_pos: sizet = 0 _g_pos: sizet = 0
_g_src: t.CPtr = None _g_src: rawptr = None
_g_src_len: sizet = 0 _g_src_len: sizet = 0
def gzip_isize(src: t.CPtr, src_len: sizet) -> uint32: def gzip_isize(src: rawptr, src_len: sizet) -> uint32:
"""gzip trailing ISIZE (little-endian u32): original length hint.""" """gzip trailing ISIZE (little-endian u32): original length hint."""
if src is None or src_len < 4: if src is None or src_len < 4:
return 0 return 0
@@ -150,7 +149,7 @@ def gzip_isize(src: t.CPtr, src_len: sizet) -> uint32:
return uint32(v64) return uint32(v64)
def _reset(src: t.CPtr, src_len: sizet): def _reset(src: rawptr, src_len: sizet):
global _g_bits, _g_bitcnt, _g_pos, _g_src, _g_src_len global _g_bits, _g_bitcnt, _g_pos, _g_src, _g_src_len
_g_bits = 0 _g_bits = 0
_g_bitcnt = 0 _g_bitcnt = 0
@@ -315,8 +314,8 @@ def _cl_order_set(arr: int*, idx: int, val: int):
# ============================================================ # ============================================================
# Core decompression (module-private) # Core decompression (module-private)
# ============================================================ # ============================================================
def _gzip_decode(src: t.CPtr, src_len: sizet, def _gzip_decode(src: rawptr, src_len: sizet,
dst: t.CPtr, dst_cap: sizet) -> LONG: dst: rawptr, dst_cap: sizet) -> LONG:
if src is None or dst is None or src_len < 18 or dst_cap == 0: if src is None or dst is None or src_len < 18 or dst_cap == 0:
return -1 return -1
ub: uchar* = src ub: uchar* = src
+2 -3
View File
@@ -1,5 +1,4 @@
import string import string
import t, c
# ============================================== # ==============================================
@@ -41,8 +40,8 @@ class md5:
state: [4]uint32 = [ state: [4]uint32 = [
0x67452301, 0xefcdab89, 0x67452301, 0xefcdab89,
0x98badcfe, 0x10325476 0x98badcfe, 0x10325476
] # 4 32-bit state registers ] # 4 32-bit state registers
count: uint64 = 0 # processed bits count: uint64 = 0 # processed bits
buf: [MD5_BLOCK_LEN]uint8 buf: [MD5_BLOCK_LEN]uint8
# Initialize MD5 context # Initialize MD5 context
def __init__(self): def __init__(self):
-2
View File
@@ -1,6 +1,4 @@
import string import string
import t, c
# ============================================== # ==============================================
-1
View File
@@ -1,5 +1,4 @@
import string import string
import t, c
# ============================================== # ==============================================
-1
View File
@@ -1,4 +1,3 @@
import t, c
import string import string
-2
View File
@@ -1,10 +1,8 @@
# hashtable - Open-addressing hash table library # hashtable - Open-addressing hash table library
# Linear probing for collision resolution, supports auto-resizing and tombstone deletion # Linear probing for collision resolution, supports auto-resizing and tombstone deletion
# Key type fixed as str (i8*), value type is void* (generic pointer) # Key type fixed as str (i8*), value type is void* (generic pointer)
import t, c
import string import string
import memhub import memhub
import stdio
from stdint import * from stdint import *
# Empty slot marker (hash == 0) # Empty slot marker (hash == 0)
+6 -7
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
@@ -26,9 +25,9 @@ class JsonValue:
int_val: int64 int_val: int64
float_val: double float_val: double
str_val: str str_val: str
next: 'JsonValue' | t.CPtr next: JsonValue*
key: str key: str
child: 'JsonValue' | t.CPtr child: JsonValue*
child_count: sizet child_count: sizet
def __new__(self, pool: memhub.MemManager*): def __new__(self, pool: memhub.MemManager*):
@@ -77,7 +76,7 @@ class JsonValue:
def __len__(self) -> sizet: def __len__(self) -> sizet:
return self.child_count return self.child_count
def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr: def __getitem__(self, key: str) -> JsonValue*:
"""Python-style get: v["key"] returns an object field.""" """Python-style get: v["key"] returns an object field."""
if self.vtype != JSON_OBJECT: if self.vtype != JSON_OBJECT:
return None return None
@@ -91,7 +90,7 @@ class JsonValue:
cur = cur.next cur = cur.next
return None return None
def __setitem__(self, key: str, val: 'JsonValue' | t.CPtr): def __setitem__(self, key: str, val: JsonValue*):
"""Python-style set: v["key"] = val.""" """Python-style set: v["key"] = val."""
if self.vtype != JSON_OBJECT or key == None or val == None: if self.vtype != JSON_OBJECT or key == None or val == None:
return return
@@ -116,7 +115,7 @@ class JsonValue:
val.key = kbuf val.key = kbuf
self._append_child(val) self._append_child(val)
def get_item(self, index: sizet) -> 'JsonValue' | t.CPtr: def get_item(self, index: sizet) -> JsonValue*:
"""Get an array element by index: v.get_item(0).""" """Get an array element by index: v.get_item(0)."""
if self.vtype != JSON_ARRAY and self.vtype != JSON_OBJECT: if self.vtype != JSON_ARRAY and self.vtype != JSON_OBJECT:
return None return None
@@ -133,7 +132,7 @@ class JsonValue:
"""Internal: allocate from current object's mpool (reserved; currently returns None).""" """Internal: allocate from current object's mpool (reserved; currently returns None)."""
return None return None
def _append_child(self, child: 'JsonValue' | t.CPtr): def _append_child(self, child: JsonValue*):
if self.child == None: if self.child == None:
self.child = child self.child = child
else: else:
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
from w32.win32base import * from w32.win32base import *
from w32.win32sync import CRITICAL_SECTION, InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection, DeleteCriticalSection, TryEnterCriticalSection from w32.win32sync import CRITICAL_SECTION, InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection, DeleteCriticalSection, TryEnterCriticalSection
-3
View File
@@ -1,6 +1,3 @@
import t, c
define IS_WINDOWS = 1 define IS_WINDOWS = 1
define IS_WIN64 = 1 define IS_WIN64 = 1
define IS_X86_64 = 1 define IS_X86_64 = 1
+1 -2
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
@@ -6,7 +5,7 @@ from stdint import *
# POSIX Type Alias # POSIX Type Alias
# Note: ssize_t has already been defined as t.CPtrDiffT in stdint.py, so we won’t repeat it here. # Note: ssize_t has already been defined as t.CPtrDiffT in stdint.py, so we won’t repeat it here.
# ============================================================================= # =============================================================================
DIR: t.CTypedef = t.CPtr # Opaque directory stream handle DIR: t.CTypedef = rawptr # Opaque directory stream handle
mode_t: t.CTypedef = uint32 # File mode/permission bits mode_t: t.CTypedef = uint32 # File mode/permission bits
pid_t: t.CTypedef = int32 # Process ID pid_t: t.CTypedef = int32 # Process ID
off_t: t.CTypedef = int64 # File offset (Linux x86_64) off_t: t.CTypedef = int64 # File offset (Linux x86_64)
+18 -19
View File
@@ -1,6 +1,5 @@
# vrandom - Hardware Random Number Generation Library # vrandom - Hardware Random Number Generation Library
# Implemented using inline assembly based on x86 RDRAND / RDSEED instructions # Implemented using inline assembly based on x86 RDRAND / RDSEED instructions
import t, c
import math import math
@@ -14,21 +13,21 @@ define RDSEED_RETRY_LIMIT = 100
# ============================================================ # ============================================================
def rdrand16() -> uint16: def rdrand16() -> uint16:
val: uint32 = 0 val: uint32 = 0
__asm__(f"rdrand {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)}", __asm__(f"rdrand {asmout(val, ASM_DESCR.OUTPUT_REG)}",
out=[asmout(val, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC]) op=[ASM_DESCR.CLOBBER_CC])
return uint16(val) return uint16(val)
def rdrand32() -> uint32: def rdrand32() -> uint32:
val: uint32 = 0 val: uint32 = 0
__asm__(f"rdrand {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)}", __asm__(f"rdrand {asmout(val, ASM_DESCR.OUTPUT_REG)}",
out=[asmout(val, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC]) op=[ASM_DESCR.CLOBBER_CC])
return val return val
def rdrand64() -> uint64: def rdrand64() -> uint64:
val: uint64 = 0 val: uint64 = 0
__asm__(f"rdrand {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)}", __asm__(f"rdrand {asmout(val, ASM_DESCR.OUTPUT_REG)}",
out=[asmout(val, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC]) op=[ASM_DESCR.CLOBBER_CC])
return val return val
@@ -39,21 +38,21 @@ def rdrand64() -> uint64:
# ============================================================ # ============================================================
def rdseed16() -> uint16: def rdseed16() -> uint16:
val: uint32 = 0 val: uint32 = 0
__asm__(f"rdseed {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)}", __asm__(f"rdseed {asmout(val, ASM_DESCR.OUTPUT_REG)}",
out=[asmout(val, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC]) op=[ASM_DESCR.CLOBBER_CC])
return uint16(val) return uint16(val)
def rdseed32() -> uint32: def rdseed32() -> uint32:
val: uint32 = 0 val: uint32 = 0
__asm__(f"rdseed {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)}", __asm__(f"rdseed {asmout(val, ASM_DESCR.OUTPUT_REG)}",
out=[asmout(val, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC]) op=[ASM_DESCR.CLOBBER_CC])
return val return val
def rdseed64() -> uint64: def rdseed64() -> uint64:
val: uint64 = 0 val: uint64 = 0
__asm__(f"rdseed {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)}", __asm__(f"rdseed {asmout(val, ASM_DESCR.OUTPUT_REG)}",
out=[asmout(val, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC]) op=[ASM_DESCR.CLOBBER_CC])
return val return val
@@ -66,9 +65,9 @@ def rdseed64() -> uint64:
def rdrand16_step(p: uint16*) -> int: def rdrand16_step(p: uint16*) -> int:
val: uint32 = 0 val: uint32 = 0
ok: uint32 = 0 ok: uint32 = 0
c.Asm(f"""rdrand {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)} __asm__(f"""rdrand {asmout(val, ASM_DESCR.OUTPUT_REG)}
setc al setc al
movzx {c.AsmOut(ok, t.ASM_DESCR.OUTPUT_REG)}, al""", movzx {asmout(ok, ASM_DESCR.OUTPUT_REG)}, al""",
out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL]) op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL])
p[0] = uint16(val) p[0] = uint16(val)
@@ -77,9 +76,9 @@ def rdrand16_step(p: uint16*) -> int:
def rdrand32_step(p: uint32*) -> int: def rdrand32_step(p: uint32*) -> int:
val: uint32 = 0 val: uint32 = 0
ok: uint32 = 0 ok: uint32 = 0
c.Asm(f"""rdrand {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)} __asm__(f"""rdrand {asmout(val, ASM_DESCR.OUTPUT_REG)}
setc al setc al
movzx {c.AsmOut(ok, t.ASM_DESCR.OUTPUT_REG)}, al""", movzx {asmout(ok, ASM_DESCR.OUTPUT_REG)}, al""",
out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL]) op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL])
p[0] = val p[0] = val
@@ -88,9 +87,9 @@ def rdrand32_step(p: uint32*) -> int:
def rdrand64_step(p: uint64*) -> int: def rdrand64_step(p: uint64*) -> int:
val: uint64 = 0 val: uint64 = 0
ok: uint32 = 0 ok: uint32 = 0
c.Asm(f"""rdrand {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)} __asm__(f"""rdrand {asmout(val, ASM_DESCR.OUTPUT_REG)}
setc al setc al
movzx {c.AsmOut(ok, t.ASM_DESCR.OUTPUT_REG)}, al""", movzx {asmout(ok, ASM_DESCR.OUTPUT_REG)}, al""",
out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL]) op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL])
p[0] = val p[0] = val
@@ -99,9 +98,9 @@ def rdrand64_step(p: uint64*) -> int:
def rdseed16_step(p: uint16*) -> int: def rdseed16_step(p: uint16*) -> int:
val: uint32 = 0 val: uint32 = 0
ok: uint32 = 0 ok: uint32 = 0
c.Asm(f"""rdseed {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)} __asm__(f"""rdseed {asmout(val, ASM_DESCR.OUTPUT_REG)}
setc al setc al
movzx {c.AsmOut(ok, t.ASM_DESCR.OUTPUT_REG)}, al""", movzx {asmout(ok, ASM_DESCR.OUTPUT_REG)}, al""",
out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL]) op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL])
p[0] = uint16(val) p[0] = uint16(val)
@@ -110,9 +109,9 @@ def rdseed16_step(p: uint16*) -> int:
def rdseed32_step(p: uint32*) -> int: def rdseed32_step(p: uint32*) -> int:
val: uint32 = 0 val: uint32 = 0
ok: uint32 = 0 ok: uint32 = 0
c.Asm(f"""rdseed {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)} __asm__(f"""rdseed {asmout(val, ASM_DESCR.OUTPUT_REG)}
setc al setc al
movzx {c.AsmOut(ok, t.ASM_DESCR.OUTPUT_REG)}, al""", movzx {asmout(ok, ASM_DESCR.OUTPUT_REG)}, al""",
out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL]) op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL])
p[0] = val p[0] = val
@@ -121,9 +120,9 @@ def rdseed32_step(p: uint32*) -> int:
def rdseed64_step(p: uint64*) -> int: def rdseed64_step(p: uint64*) -> int:
val: uint64 = 0 val: uint64 = 0
ok: uint32 = 0 ok: uint32 = 0
c.Asm(f"""rdseed {c.AsmOut(val, t.ASM_DESCR.OUTPUT_REG)} __asm__(f"""rdseed {asmout(val, ASM_DESCR.OUTPUT_REG)}
setc al setc al
movzx {c.AsmOut(ok, t.ASM_DESCR.OUTPUT_REG)}, al""", movzx {asmout(ok, ASM_DESCR.OUTPUT_REG)}, al""",
out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)], out=[asmout(val, ASM_DESCR.OUTPUT_REG), asmout(ok, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL]) op=[ASM_DESCR.CLOBBER_CC, ASM_DESCR.CLOBBER_AL])
p[0] = val p[0] = val
+1 -2
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
from w32.win32base import * from w32.win32base import *
from w32.win32sync import ( from w32.win32sync import (
@@ -38,7 +37,7 @@ class RWLock:
def release_write(self): def release_write(self):
ReleaseSRWLockExclusive(&self.lock) ReleaseSRWLockExclusive(&self.lock)
def __enter__(self) -> 'RWLock' | t.CPtr: def __enter__(self) -> RWLock*:
self.acquire_write() self.acquire_write()
return self return self
+10 -22
View File
@@ -25,24 +25,19 @@ _mbuddy: memhub.MemBuddy*
def copyfile(src: str, dst: str) -> int: def copyfile(src: str, dst: str) -> int:
"""Copy file content. Returns 0, otherwise -1.""" """Copy file content. Returns 0, otherwise -1."""
if platmacro.IS_WINDOWS: return _copyfile_win32(src, dst) if platmacro.IS_WINDOWS else _copyfile_posix(src, dst)
return _copyfile_win32(src, dst)
return _copyfile_posix(src, dst)
def _copyfile_win32(src: str, dst: str) -> int: def _copyfile_win32(src: str, dst: str) -> int:
"""Windows: Use CopyFileA kernel implementation (efficient).""" """Windows: Use CopyFileA kernel implementation (efficient)."""
result: BOOL = w32.win32file.CopyFileA(src, dst, 0) result: BOOL = w32.win32file.CopyFileA(src, dst, 0)
if result == 0: return -1 if result == 0 else 0
return -1
return 0
def _copyfile_posix(src: str, dst: str) -> int: def _copyfile_posix(src: str, dst: str) -> int:
"""POSIX: Manual file read/write loop copy""" """POSIX: Manual file read/write loop copy"""
fd_in: INT = posix.open(src, posix.O_RDONLY, 0) fd_in: INT = posix.open(src, posix.O_RDONLY, 0)
if fd_in < 0: if fd_in < 0: return -1
return -1
fd_out: INT = posix.open(dst, posix.O_WRONLY | posix.O_CREAT | posix.O_TRUNC, 0o644) fd_out: INT = posix.open(dst, posix.O_WRONLY | posix.O_CREAT | posix.O_TRUNC, 0o644)
if fd_out < 0: if fd_out < 0:
posix.close(fd_in) posix.close(fd_in)
@@ -52,17 +47,16 @@ def _copyfile_posix(src: str, dst: str) -> int:
posix.close(fd_in) posix.close(fd_in)
posix.close(fd_out) posix.close(fd_out)
return -1 return -1
n: ssize_t = 0 n: sizet = 0
result: int = 0 result: int = 0
n = posix.read(fd_in, buf, COPY_BUF_SIZE) n = posix.read(fd_in, buf, COPY_BUF_SIZE)
while n > 0: while n > 0:
written: ssize_t = posix.write(fd_out, buf, sizet(n)) written: sizet = posix.write(fd_out, buf, sizet(n))
if written < 0: if written < 0:
result = -1 result = -1
break break
n = posix.read(fd_in, buf, COPY_BUF_SIZE) n = posix.read(fd_in, buf, COPY_BUF_SIZE)
if n < 0: if n < 0: result = -1
result = -1
_mbuddy.free(buf) _mbuddy.free(buf)
posix.close(fd_in) posix.close(fd_in)
posix.close(fd_out) posix.close(fd_out)
@@ -75,9 +69,7 @@ def _copyfile_posix(src: str, dst: str) -> int:
def copy(src: str, dst: str) -> int: def copy(src: str, dst: str) -> int:
"""Copy file and preserve permissions/metadata (POSIX only). Returns 0, otherwise -1.""" """Copy file and preserve permissions/metadata (POSIX only). Returns 0, otherwise -1."""
if platmacro.IS_WINDOWS: return _copy_win32(src, dst) if platmacro.IS_WINDOWS else _copy_posix(src, dst)
return _copy_win32(src, dst)
return _copy_posix(src, dst)
def _copy_win32(src: str, dst: str) -> int: def _copy_win32(src: str, dst: str) -> int:
@@ -88,11 +80,9 @@ def _copy_win32(src: str, dst: str) -> int:
def _copy_posix(src: str, dst: str) -> int: def _copy_posix(src: str, dst: str) -> int:
"""POSIX: Copy file and preserve permissions (stat+chmod). Returns 0, otherwise -1.""" """POSIX: Copy file and preserve permissions (stat+chmod). Returns 0, otherwise -1."""
result: int = _copyfile_posix(src, dst) result: int = _copyfile_posix(src, dst)
if result != 0: if result != 0: return -1
return -1
st: posix.Stat = posix.Stat() st: posix.Stat = posix.Stat()
if posix.stat(src, &st) == 0: if posix.stat(src, &st) == 0: posix.chmod(dst, st.st_mode)
posix.chmod(dst, st.st_mode)
return 0 return 0
@@ -102,9 +92,7 @@ def _copy_posix(src: str, dst: str) -> int:
def rmtree(path: str) -> int: def rmtree(path: str) -> int:
"""Recursively delete a directory tree. Returns 0 on success and -1 on failure.""" """Recursively delete a directory tree. Returns 0 on success and -1 on failure."""
if platmacro.IS_WINDOWS: return _rmtree_win32(path) if platmacro.IS_WINDOWS else - _rmtree_posix(path)
return _rmtree_win32(path)
return _rmtree_posix(path)
def _rmtree_win32(path: str) -> int: def _rmtree_win32(path: str) -> int:
+5 -6
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
import string import string
import platmacro import platmacro
@@ -127,15 +126,15 @@ def _MakeAddr(host: str, port: INT) -> SocketAddr:
if he_raw is not None: if he_raw is not None:
# he_raw.h_addr_list → he_raw->h_addr_list (char**) # he_raw.h_addr_list → he_raw->h_addr_list (char**)
# c.Deref(uint64(he_raw.h_addr_list, t.CPtr)) → *(uint64_t*)(he_raw->h_addr_list) → h_addr_list[0] # c.Deref(uint64(he_raw.h_addr_list, t.CPtr)) → *(uint64_t*)(he_raw->h_addr_list) → h_addr_list[0]
ip_ptr: u64 = u64(c.Deref(uint64(he_raw.h_addr_list, t.CPtr))) ip_ptr: u64 = u64(*(uint64*(he_raw.h_addr_list)))
if ip_ptr != 0: if ip_ptr != 0:
addr.addr = u32(c.Deref(uint32(ip_ptr, t.CPtr))) addr.addr = u32(*(uint32*(ip_ptr)))
else: else:
he: HostEnt* = gethostbyname(host) he: HostEnt* = gethostbyname(host)
if he is not None: if he is not None:
ip_ptr: u64 = u64(c.Deref(uint64(he.h_addr_list, t.CPtr))) ip_ptr: u64 = u64(*(uint64*(he.h_addr_list)))
if ip_ptr != 0: if ip_ptr != 0:
addr.addr = u32(c.Deref(uint32(ip_ptr, t.CPtr))) addr.addr = u32(*(uint32*(ip_ptr)))
return addr return addr
@@ -175,7 +174,7 @@ def _ParseIPv4(ip_str: str, out_addr: u32*) -> bool:
result = result | (u32(parts[1]) << 16) result = result | (u32(parts[1]) << 16)
result = result | (u32(parts[2]) << 8) result = result | (u32(parts[2]) << 8)
result = result | u32(parts[3]) result = result | u32(parts[3])
c.DerefAs(out_addr, result) *out_addr = result
return True return True
+48 -52
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import * from stdint import *
from w32.win32base import * from w32.win32base import *
import string import string
@@ -525,8 +524,8 @@ class TracebackState():
ContextBuf: VOIDPTR # CONTEXT buffer (calloc, 16-byte aligned) ContextBuf: VOIDPTR # CONTEXT buffer (calloc, 16-byte aligned)
FrameBuf: VOIDPTR # STACKFRAME64 buffer (calloc) FrameBuf: VOIDPTR # STACKFRAME64 buffer (calloc)
LineBuf: bytes # output line buffer LineBuf: bytes # output line buffer
FrameAddr: t.CArray[uint64, TRACEBACK_FRAME_BUF] # collected frames, innermost first FrameAddr: [TRACEBACK_FRAME_BUF]uint64 # collected frames, innermost first
FrameScan: t.CArray[int32, TRACEBACK_FRAME_BUF] # 0 = authoritative, 1 = inferred (ScanStack) FrameScan: [TRACEBACK_FRAME_BUF]int32 # 0 = authoritative, 1 = inferred (ScanStack)
FrameCount: int # frames collected so far (also the printed frame count) FrameCount: int # frames collected so far (also the printed frame count)
@@ -577,7 +576,7 @@ def DiagEnabled() -> int:
global _DiagFlag global _DiagFlag
if _DiagFlag >= 0: if _DiagFlag >= 0:
return _DiagFlag return _DiagFlag
Val: t.CArray[char, 8] Val: [8]char
Val[0] = char(0) Val[0] = char(0)
R: DWORD = GetEnvironmentVariableA("TRACEBACK_DIAG", &Val, DWORD(8)) R: DWORD = GetEnvironmentVariableA("TRACEBACK_DIAG", &Val, DWORD(8))
_DiagFlag = 0 _DiagFlag = 0
@@ -630,7 +629,7 @@ def EmitToFile(N: int):
if _DumpFileFailWarned != 0: if _DumpFileFailWarned != 0:
return return
_DumpFileFailWarned = 1 _DumpFileFailWarned = 1
LB: t.CArray[char, 160] LB: [160]char
M: int = stdio.snprintf(&LB, sizet(160), M: int = stdio.snprintf(&LB, sizet(160),
"[traceback] WARN: stack dump file write failed (further file-write failures not reported)\n") "[traceback] WARN: stack dump file write failed (further file-write failures not reported)\n")
if M <= 0: if M <= 0:
@@ -667,15 +666,15 @@ def EmitLine(N: int):
# Line-number prefix: shift the buffer right by the prefix length, then write the prefix. # Line-number prefix: shift the buffer right by the prefix length, then write the prefix.
if DiagEnabled() != 0: if DiagEnabled() != 0:
_DumpLineNo = _DumpLineNo + 1 _DumpLineNo = _DumpLineNo + 1
Pfx: t.CArray[char, 32] Pfx: [32]char
Pl: int = stdio.snprintf(&Pfx, sizet(32), " [dump#%d] ", _DumpLineNo) Pl: int = stdio.snprintf(&Pfx, sizet(32), " [dump#%d] ", _DumpLineNo)
if Pl > 0: if Pl > 0:
# Keep room for the trailing '\0' after prefixing. # Keep room for the trailing '\0' after prefixing.
if N + Pl > TRACEBACK_LINE_CAP - 1: if N + Pl > TRACEBACK_LINE_CAP - 1:
N = TRACEBACK_LINE_CAP - 1 - Pl N = TRACEBACK_LINE_CAP - 1 - Pl
# Move the trailing '\0' too (N+1) so the buffer stays NUL-terminated. # Move the trailing '\0' too (N+1) so the buffer stays NUL-terminated.
string.memmove(t.CPtr(_State.LineBuf + int(Pl)), t.CPtr(_State.LineBuf), sizet(N + 1)) string.memmove(rawptr(_State.LineBuf + int(Pl)), rawptr(_State.LineBuf), sizet(N + 1))
string.memmove(t.CPtr(_State.LineBuf), &Pfx, sizet(Pl)) string.memmove(rawptr(_State.LineBuf), &Pfx, sizet(Pl))
N = N + Pl N = N + Pl
WriteConsoleOnly(N) WriteConsoleOnly(N)
EmitToFile(N) EmitToFile(N)
@@ -718,7 +717,7 @@ def EmitEarlyCode(Text: str, Code: ULONG):
N: int = stdio.snprintf(_State.LineBuf, sizet(TRACEBACK_LINE_CAP), "%s(GetLastError=%u)\n", Text, Code) N: int = stdio.snprintf(_State.LineBuf, sizet(TRACEBACK_LINE_CAP), "%s(GetLastError=%u)\n", Text, Code)
EmitLine(N) EmitLine(N)
return return
LB: t.CArray[char, 256] LB: [256]char
N2: int = stdio.snprintf(&LB, sizet(256), "%s(GetLastError=%u)\n", Text, Code) N2: int = stdio.snprintf(&LB, sizet(256), "%s(GetLastError=%u)\n", Text, Code)
if N2 <= 0: if N2 <= 0:
return return
@@ -890,7 +889,7 @@ def LoadMapFile(hFile: HANDLE, Size: int64) -> int:
Got: ULONG = 0 Got: ULONG = 0
if wf.ReadFile(hFile, Buf, ULONG(Size), &Got, None) == 0: if wf.ReadFile(hFile, Buf, ULONG(Size), &Got, None) == 0:
Err: ULONG = GetLastError() Err: ULONG = GetLastError()
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
if _State != None and _State.LineBuf != None: if _State != None and _State.LineBuf != None:
RN: int = stdio.snprintf(_State.LineBuf, sizet(TRACEBACK_LINE_CAP), RN: int = stdio.snprintf(_State.LineBuf, sizet(TRACEBACK_LINE_CAP),
"[traceback] WARN: read <exe>.stackmap failed (GetLastError=%u)," "[traceback] WARN: read <exe>.stackmap failed (GetLastError=%u),"
@@ -898,14 +897,14 @@ def LoadMapFile(hFile: HANDLE, Size: int64) -> int:
EmitLine(RN) EmitLine(RN)
return 0 return 0
if sizet(Got) != sizet(Size): if sizet(Got) != sizet(Size):
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailPlain(Size, "read fewer bytes than the file size") return MapFailPlain(Size, "read fewer bytes than the file size")
# Header: magic / version / entry_count / str_count / str_bytes # Header: magic / version / entry_count / str_count / str_bytes
if ReadU32(Buf, 0) != uint32(TRACEBACK_MAP_MAGIC): if ReadU32(Buf, 0) != uint32(TRACEBACK_MAP_MAGIC):
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailHdr(Size, "magic is not \"TPCS\" (writer/reader endianness or offset mismatch)", Buf) return MapFailHdr(Size, "magic is not \"TPCS\" (writer/reader endianness or offset mismatch)", Buf)
if ReadU32(Buf, 4) != uint32(TRACEBACK_MAP_VERSION): if ReadU32(Buf, 4) != uint32(TRACEBACK_MAP_VERSION):
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailHdr(Size, "version is not 1 (stackmap written by another TPC version)", Buf) return MapFailHdr(Size, "version is not 1 (stackmap written by another TPC version)", Buf)
Cnt: uint32 = ReadU32(Buf, 8) Cnt: uint32 = ReadU32(Buf, 8)
StrCnt: uint32 = ReadU32(Buf, 12) StrCnt: uint32 = ReadU32(Buf, 12)
@@ -915,16 +914,16 @@ def LoadMapFile(hFile: HANDLE, Size: int64) -> int:
+ uint64(Cnt) * uint64(TRACEBACK_ENTRY_SIZE) \ + uint64(Cnt) * uint64(TRACEBACK_ENTRY_SIZE) \
+ uint64(SBytes) + uint64(SBytes)
if Need > uint64(Size): if Need > uint64(Size):
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailHdr(Size, "20+entry_count*20+str_bytes exceeds end of file " return MapFailHdr(Size, "20+entry_count*20+str_bytes exceeds end of file "
"(header corrupt or file truncated)", Buf) "(header corrupt or file truncated)", Buf)
if SBytes == 0: if SBytes == 0:
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailHdr(Size, "str_bytes is 0 (string pool missing)", Buf) return MapFailHdr(Size, "str_bytes is 0 (string pool missing)", Buf)
# The last pool byte must be '\0' so any later %s print stays inside the pool. # The last pool byte must be '\0' so any later %s print stays inside the pool.
PoolEnd: int = TRACEBACK_MAP_HEADER + int(Cnt) * TRACEBACK_ENTRY_SIZE + int(SBytes) - 1 PoolEnd: int = TRACEBACK_MAP_HEADER + int(Cnt) * TRACEBACK_ENTRY_SIZE + int(SBytes) - 1
if Buf[PoolEnd] != 0: if Buf[PoolEnd] != 0:
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailHdr(Size, "string pool does not end with '\\0' (pool corrupt)", Buf) return MapFailHdr(Size, "string pool does not end with '\\0' (pool corrupt)", Buf)
# Full-table check: strictly ascending rva + string offsets inside the pool. # Full-table check: strictly ascending rva + string offsets inside the pool.
Prev: uint64 = 0 Prev: uint64 = 0
@@ -932,17 +931,17 @@ def LoadMapFile(hFile: HANDLE, Size: int64) -> int:
Off: int = TRACEBACK_MAP_HEADER + int(i) * TRACEBACK_ENTRY_SIZE Off: int = TRACEBACK_MAP_HEADER + int(i) * TRACEBACK_ENTRY_SIZE
Cur: uint64 = ReadU64(Buf, Off) Cur: uint64 = ReadU64(Buf, Off)
if i > 0 and Cur <= Prev: if i > 0 and Cur <= Prev:
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailEntry(Size, int(i), return MapFailEntry(Size, int(i),
"record rva not strictly ascending (breaks binary search)", Cur) "record rva not strictly ascending (breaks binary search)", Cur)
Prev = Cur Prev = Cur
FO: uint32 = ReadU32(Buf, Off + TRACEBACK_ENTRY_FILEOFF) FO: uint32 = ReadU32(Buf, Off + TRACEBACK_ENTRY_FILEOFF)
FN: uint32 = ReadU32(Buf, Off + TRACEBACK_ENTRY_FUNCOFF) FN: uint32 = ReadU32(Buf, Off + TRACEBACK_ENTRY_FUNCOFF)
if uint64(FO) >= uint64(SBytes): if uint64(FO) >= uint64(SBytes):
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailEntry(Size, int(i), "file_off outside the string pool", uint64(FO)) return MapFailEntry(Size, int(i), "file_off outside the string pool", uint64(FO))
if FN != 0 and uint64(FN) >= uint64(SBytes): if FN != 0 and uint64(FN) >= uint64(SBytes):
stdlib.free(t.CPtr(Buf)) stdlib.free(Buf)
return MapFailEntry(Size, int(i), return MapFailEntry(Size, int(i),
"func_off is neither 0 (no name) nor inside the string pool", "func_off is neither 0 (no name) nor inside the string pool",
uint64(FN)) uint64(FN))
@@ -989,13 +988,13 @@ def EmitSourceText(Path: str, LineNo: int):
if _State == None or _State.LineBuf == None or LineNo <= 0: if _State == None or _State.LineBuf == None or LineNo <= 0:
return return
h: HANDLE = wf.CreateFileA(Path, wf.GENERIC_READ, wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE, h: HANDLE = wf.CreateFileA(Path, wf.GENERIC_READ, wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE,
t.CPtr(0), wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, t.CPtr(0)) None, wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
if h == INVALID_HANDLE_VALUE: if h == INVALID_HANDLE_VALUE:
return return
Chunk: t.CArray[char, TRACEBACK_SRC_CHUNK] Chunk: [TRACEBACK_SRC_CHUNK]char
Src: t.CArray[char, TRACEBACK_LINE_CAP] Src: [TRACEBACK_LINE_CAP]char
Line: int = 1 Line: int = 1
Out: int = 0 # bytes collected for the target line Out: int = 0 # bytes collected for the target line
Done: int = 0 Done: int = 0
while Done == 0: while Done == 0:
Got: ULONG = 0 Got: ULONG = 0
@@ -1115,7 +1114,7 @@ def EmitFrame(Addr: uint64, Base: uint64, Scanned: int):
# ============================================================ # ============================================================
def ReadMemCb(hProcess: HANDLE, Addr: ULONGLONG, Buf: VOIDPTR, Size: DWORD, def ReadMemCb(hProcess: HANDLE, Addr: ULONGLONG, Buf: VOIDPTR, Size: DWORD,
pRead: t.CPtr) -> BOOL: pRead: rawptr) -> BOOL:
"""This module's stand-in for the StackWalk64 memory callback (PREAD_PROCESS_MEMORY_ROUTINE64). """This module's stand-in for the StackWalk64 memory callback (PREAD_PROCESS_MEMORY_ROUTINE64).
Not kernel32!ReadProcessMemory directly: Not kernel32!ReadProcessMemory directly:
@@ -1129,7 +1128,7 @@ def ReadMemCb(hProcess: HANDLE, Addr: ULONGLONG, Buf: VOIDPTR, Size: DWORD,
Passing this function by name as a t.CPtr mirrors SetConsoleCtrlHandler(CtrlHandler). Passing this function by name as a t.CPtr mirrors SetConsoleCtrlHandler(CtrlHandler).
""" """
Read: sizet = 0 Read: sizet = 0
R: BOOL = wm.ReadProcessMemory(hProcess, t.CPtr(Addr), Buf, sizet(Size), &Read) R: BOOL = wm.ReadProcessMemory(hProcess, rawptr(Addr), Buf, sizet(Size), &Read)
if pRead != None: if pRead != None:
Rd: uint32* = (uint32*)(pRead) Rd: uint32* = (uint32*)(pRead)
Rd[0] = uint32(Read) Rd[0] = uint32(Read)
@@ -1299,7 +1298,7 @@ def WalkRbpChain(Rip: uint64, Rbp: uint64, Base: uint64) -> int:
Cur: uint64 = Rbp Cur: uint64 = Rbp
i: int = 1 i: int = 1
HProc: HANDLE = wp.GetCurrentProcess() HProc: HANDLE = wp.GetCurrentProcess()
Two: t.CArray[uint64, 2] # [0] = [rbp] (caller rbp), [1] = [rbp+8] (return address) Two: [2]uint64 # [0] = [rbp] (caller rbp), [1] = [rbp+8] (return address)
while i < TRACEBACK_MAX_FRAMES: while i < TRACEBACK_MAX_FRAMES:
# Raw readings + decision for this step, produced before any early reject. # Raw readings + decision for this step, produced before any early reject.
Rd: sizet = 0 Rd: sizet = 0
@@ -1319,7 +1318,7 @@ def WalkRbpChain(Rip: uint64, Rbp: uint64, Base: uint64) -> int:
# Read 16 bytes via ReadProcessMemory (invalid/unreadable addresses return 0 without # Read 16 bytes via ReadProcessMemory (invalid/unreadable addresses return 0 without
# faulting); dereferencing a wild rbp directly would crash, which the primary path # faulting); dereferencing a wild rbp directly would crash, which the primary path
# must never do. # must never do.
OkRead: BOOL = wm.ReadProcessMemory(HProc, t.CPtr(Cur), &Two, sizet(16), &Rd) OkRead: BOOL = wm.ReadProcessMemory(HProc, rawptr(Cur), &Two, sizet(16), &Rd)
if OkRead == 0 or sizet(Rd) < sizet(16): if OkRead == 0 or sizet(Rd) < sizet(16):
# GetLastError must be read immediately after ReadProcessMemory. # GetLastError must be read immediately after ReadProcessMemory.
RpmErr = GetLastError() RpmErr = GetLastError()
@@ -1401,8 +1400,8 @@ def ScanStack(StartIndex: int, Rsp: uint64, Base: uint64) -> int:
HProc: HANDLE = wp.GetCurrentProcess() HProc: HANDLE = wp.GetCurrentProcess()
Upper: uint64 = Rsp + uint64(TRACEBACK_SCAN_MAX_BYTES) Upper: uint64 = Rsp + uint64(TRACEBACK_SCAN_MAX_BYTES)
ModEnd: uint64 = Base + uint64(TRACEBACK_MODULE_MAX) ModEnd: uint64 = Base + uint64(TRACEBACK_MODULE_MAX)
Words: t.CArray[uint64, TRACEBACK_SCAN_CHUNK_WORDS] # 4 KiB chunk buffer (local) Words: [TRACEBACK_SCAN_CHUNK_WORDS]uint64 # 4 KiB chunk buffer (local)
Found: t.CArray[uint64, TRACEBACK_SCAN_MAX_HITS] # deduplicated candidate return addresses Found: [TRACEBACK_SCAN_MAX_HITS]uint64 # deduplicated candidate return addresses
Hits: int = 0 Hits: int = 0
Prev: uint64 = 0 # [A-8]: previous word unknown at chunk start, 0 (one less confirmation) Prev: uint64 = 0 # [A-8]: previous word unknown at chunk start, 0 (one less confirmation)
Addr: uint64 = Rsp Addr: uint64 = Rsp
@@ -1414,7 +1413,7 @@ def ScanStack(StartIndex: int, Rsp: uint64, Base: uint64) -> int:
EmitLine(D0) EmitLine(D0)
while Addr < Upper: while Addr < Upper:
Rd: sizet = 0 Rd: sizet = 0
OkRead: BOOL = wm.ReadProcessMemory(HProc, t.CPtr(Addr), &Words, sizet(TRACEBACK_SCAN_CHUNK_BYTES), &Rd) OkRead: BOOL = wm.ReadProcessMemory(HProc, rawptr(Addr), &Words, sizet(TRACEBACK_SCAN_CHUNK_BYTES), &Rd)
N: int = int(sizet(Rd) / sizet(8)) # u64 words actually read in this chunk N: int = int(sizet(Rd) / sizet(8)) # u64 words actually read in this chunk
if OkRead == 0 or N <= 0: if OkRead == 0 or N <= 0:
Er: ULONG = GetLastError() Er: ULONG = GetLastError()
@@ -1630,7 +1629,7 @@ def DumpMainThread(IsBreak: int) -> int:
string.memset(_State.ContextBuf, 0, wp.CONTEXT.__sizeof__()) string.memset(_State.ContextBuf, 0, wp.CONTEXT.__sizeof__())
ctx: wp.CONTEXT* = (wp.CONTEXT*)(_State.ContextBuf) ctx: wp.CONTEXT* = (wp.CONTEXT*)(_State.ContextBuf)
ctx.ContextFlags = uint32(wp.CONTEXT_FULL) ctx.ContextFlags = uint32(wp.CONTEXT_FULL)
if wp.GetThreadContext(hThread, t.CPtr(_State.ContextBuf)) == 0: if wp.GetThreadContext(hThread, rawptr(_State.ContextBuf)) == 0:
N = stdio.snprintf(_State.LineBuf, sizet(TRACEBACK_LINE_CAP), N = stdio.snprintf(_State.LineBuf, sizet(TRACEBACK_LINE_CAP),
" fallback: GetThreadContext failed (GetLastError=%u), cannot dump stack\n", " fallback: GetThreadContext failed (GetLastError=%u), cannot dump stack\n",
GetLastError()) GetLastError())
@@ -1749,8 +1748,8 @@ def DumpMainThread(IsBreak: int) -> int:
# shrink the dump whenever dbghelp fails. Locals only: the handler thread allocates # shrink the dump whenever dbghelp fails. Locals only: the handler thread allocates
# nothing (same discipline as ScanStack's chunk buffers). # nothing (same discipline as ScanStack's chunk buffers).
SavedCount: int = _State.FrameCount SavedCount: int = _State.FrameCount
SaveAddrBuf: t.CArray[uint64, TRACEBACK_FRAME_BUF] SaveAddrBuf: [TRACEBACK_FRAME_BUF]uint64
SaveScanBuf: t.CArray[int32, TRACEBACK_FRAME_BUF] SaveScanBuf: [TRACEBACK_FRAME_BUF]int32
for si in range(SavedCount): for si in range(SavedCount):
SaveAddrBuf[si] = _State.FrameAddr[si] SaveAddrBuf[si] = _State.FrameAddr[si]
SaveScanBuf[si] = _State.FrameScan[si] SaveScanBuf[si] = _State.FrameScan[si]
@@ -1832,11 +1831,10 @@ def DumpCrashThread(Info: VOIDPTR) -> int:
EmitText("=== TPC crash dump (unhandled exception; the process keeps terminating) ===\n") EmitText("=== TPC crash dump (unhandled exception; the process keeps terminating) ===\n")
HProc: HANDLE = wp.GetCurrentProcess() HProc: HANDLE = wp.GetCurrentProcess()
Rd: sizet = 0 Rd: sizet = 0
Two: t.CArray[uint64, 2] # [0] = EXCEPTION_RECORD*, [1] = CONTEXT* (EXCEPTION_POINTERS) Two: [2]uint64 # [0] = EXCEPTION_RECORD*, [1] = CONTEXT* (EXCEPTION_POINTERS)
Two[0] = 0 Two[0] = 0
Two[1] = 0 Two[1] = 0
if wm.ReadProcessMemory(HProc, t.CPtr(Info), &Two, sizet(16), &Rd) == 0 \ if wm.ReadProcessMemory(HProc, rawptr(Info), &Two, sizet(16), &Rd) == 0 or sizet(Rd) < sizet(16):
or sizet(Rd) < sizet(16):
EmitText(" fallback: EXCEPTION_POINTERS unreadable, crash dump skipped\n") EmitText(" fallback: EXCEPTION_POINTERS unreadable, crash dump skipped\n")
AtomicLeaveDump() AtomicLeaveDump()
return 0 return 0
@@ -1853,10 +1851,10 @@ def DumpCrashThread(Info: VOIDPTR) -> int:
# u32[10..11] ExceptionInformation[1] (offset 0x28 = the faulting DATA address). # u32[10..11] ExceptionInformation[1] (offset 0x28 = the faulting DATA address).
# The two ExceptionInformation fields are only filled for access violations (code 0xC0000005); # The two ExceptionInformation fields are only filled for access violations (code 0xC0000005);
# they are printed unconditionally because a hard crash is exactly when raw numbers beat guesses. # they are printed unconditionally because a hard crash is exactly when raw numbers beat guesses.
RecWords: t.CArray[uint32, 12] RecWords: [12]uint32
RecWords[0] = 0 RecWords[0] = 0
if Rec != 0: if Rec != 0:
if wm.ReadProcessMemory(HProc, t.CPtr(Rec), &RecWords, sizet(48), &Rd) != 0 \ if wm.ReadProcessMemory(HProc, rawptr(Rec), &RecWords, sizet(48), &Rd) != 0 \
and sizet(Rd) >= sizet(48): and sizet(Rd) >= sizet(48):
Code = RecWords[0] Code = RecWords[0]
ExAddr = uint64(RecWords[4]) | (uint64(RecWords[5]) << 32) ExAddr = uint64(RecWords[4]) | (uint64(RecWords[5]) << 32)
@@ -1871,9 +1869,7 @@ def DumpCrashThread(Info: VOIDPTR) -> int:
EmitText(" fallback: CONTEXT pointer null, crash dump skipped\n") EmitText(" fallback: CONTEXT pointer null, crash dump skipped\n")
AtomicLeaveDump() AtomicLeaveDump()
return 0 return 0
if wm.ReadProcessMemory(HProc, t.CPtr(CtxPtr), t.CPtr(_State.ContextBuf), if wm.ReadProcessMemory(HProc, rawptr(CtxPtr), rawptr(_State.ContextBuf), wp.CONTEXT.__sizeof__(), &Rd) == 0 or sizet(Rd) < sizet(wp.CONTEXT.__sizeof__()):
wp.CONTEXT.__sizeof__(), &Rd) == 0 \
or sizet(Rd) < sizet(wp.CONTEXT.__sizeof__()):
EmitText(" fallback: CONTEXT unreadable, crash dump skipped\n") EmitText(" fallback: CONTEXT unreadable, crash dump skipped\n")
AtomicLeaveDump() AtomicLeaveDump()
return 0 return 0
@@ -1906,7 +1902,7 @@ def DumpCrashThread(Info: VOIDPTR) -> int:
# follow. The RBP chain and the stack scan stay as fallbacks, exactly as in the Ctrl+C path. # follow. The RBP chain and the stack scan stay as fallbacks, exactly as in the Ctrl+C path.
Frames: int = 0 Frames: int = 0
if DbgHelpUsable() != 0: if DbgHelpUsable() != 0:
Frames = WalkViaDbgHelp(HProc, wp.GetCurrentThread(), t.CPtr(_State.ContextBuf), Base) Frames = WalkViaDbgHelp(HProc, wp.GetCurrentThread(), rawptr(_State.ContextBuf), Base)
if Frames <= 1: if Frames <= 1:
_State.FrameCount = 0 _State.FrameCount = 0
Frames = WalkRbpChain(Rip, Rbp, Base) Frames = WalkRbpChain(Rip, Rbp, Base)
@@ -1993,20 +1989,20 @@ def Install() -> int:
return 0 return 0
Plen: ULONG = wf.GetModuleFileNameA(None, Pb, ULONG(TRACEBACK_PATH_CAP)) Plen: ULONG = wf.GetModuleFileNameA(None, Pb, ULONG(TRACEBACK_PATH_CAP))
if Plen == 0 or sizet(Plen) >= sizet(TRACEBACK_PATH_CAP): if Plen == 0 or sizet(Plen) >= sizet(TRACEBACK_PATH_CAP):
stdlib.free(t.CPtr(Pb)) stdlib.free(Pb)
EmitEarly("[traceback] WARN: GetModuleFileNameA failed, ctrl handler not installed\n") EmitEarly("[traceback] WARN: GetModuleFileNameA failed, ctrl handler not installed\n")
return 0 return 0
if sizet(Plen) + 16 > sizet(TRACEBACK_PATH_CAP): if sizet(Plen) + 16 > sizet(TRACEBACK_PATH_CAP):
# 16 bytes of slack covers both ".stackmap" (10) and ".stackdump.txt" (16). # 16 bytes of slack covers both ".stackmap" (10) and ".stackdump.txt" (16).
stdlib.free(t.CPtr(Pb)) stdlib.free(Pb)
EmitEarly("[traceback] WARN: exe path too long to append suffix safely, ctrl handler not installed\n") EmitEarly("[traceback] WARN: exe path too long to append suffix safely, ctrl handler not installed\n")
return 0 return 0
string.strcpy(str(Pb + int(Plen)), ".stackmap") string.strcpy(str(Pb + int(Plen)), ".stackmap")
# (2) sidecar existence = master switch (absent -> return immediately, no side effects). # (2) sidecar existence = master switch (absent -> return immediately, no side effects).
hFile: HANDLE = wf.CreateFileA(Pb, wf.GENERIC_READ, wf.FILE_SHARE_READ, t.CPtr(0), wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, t.CPtr(0)) hFile: HANDLE = wf.CreateFileA(Pb, wf.GENERIC_READ, wf.FILE_SHARE_READ, None, wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
if hFile == INVALID_HANDLE_VALUE: if hFile == INVALID_HANDLE_VALUE:
Err0: ULONG = GetLastError() Err0: ULONG = GetLastError()
stdlib.free(t.CPtr(Pb)) stdlib.free(Pb)
if Err0 != ULONG(ERROR_FILE_NOT_FOUND): if Err0 != ULONG(ERROR_FILE_NOT_FOUND):
EmitEarlyCode("[traceback] WARN: cannot open <exe>.stackmap, ctrl handler not installed ", Err0) EmitEarlyCode("[traceback] WARN: cannot open <exe>.stackmap, ctrl handler not installed ", Err0)
return 0 return 0
@@ -2015,14 +2011,14 @@ def Install() -> int:
# __sizeof__() allocation below covers them and the memset zeroes FrameCount. # __sizeof__() allocation below covers them and the memset zeroes FrameCount.
_State = stdlib.malloc(TracebackState.__sizeof__()) _State = stdlib.malloc(TracebackState.__sizeof__())
if _State == None: if _State == None:
stdlib.free(t.CPtr(Pb)) stdlib.free(Pb)
CloseHandle(hFile) CloseHandle(hFile)
EmitEarly("[traceback] WARN: state alloc failed, ctrl handler not installed\n") EmitEarly("[traceback] WARN: state alloc failed, ctrl handler not installed\n")
return 0 return 0
string.memset(_State, 0, TracebackState.__sizeof__()) string.memset(_State, 0, TracebackState.__sizeof__())
_State.LineBuf = stdlib.malloc(TRACEBACK_LINE_CAP) _State.LineBuf = stdlib.malloc(TRACEBACK_LINE_CAP)
_State.ContextBuf = t.CPtr(stdlib.calloc(1, wp.CONTEXT.__sizeof__())) _State.ContextBuf = rawptr(stdlib.calloc(1, wp.CONTEXT.__sizeof__()))
_State.FrameBuf = t.CPtr(stdlib.calloc(1, dbg.STACKFRAME64.__sizeof__())) _State.FrameBuf = rawptr(stdlib.calloc(1, dbg.STACKFRAME64.__sizeof__()))
_State.OutHandle = wf.GetStdHandle(wf.STD_ERROR_HANDLE) _State.OutHandle = wf.GetStdHandle(wf.STD_ERROR_HANDLE)
if _State.OutHandle == None or _State.OutHandle == INVALID_HANDLE_VALUE: if _State.OutHandle == None or _State.OutHandle == INVALID_HANDLE_VALUE:
_State.OutHandle = wf.GetStdHandle(wf.STD_OUTPUT_HANDLE) _State.OutHandle = wf.GetStdHandle(wf.STD_OUTPUT_HANDLE)
@@ -2033,7 +2029,7 @@ def Install() -> int:
# Name the missing buffer now (not later in the callback thread); any missing buffer makes # Name the missing buffer now (not later in the callback thread); any missing buffer makes
# Install return 0. _State exists here, so EmitEarly forwards to EmitText; if LineBuf is # Install return 0. _State exists here, so EmitEarly forwards to EmitText; if LineBuf is
# the missing one, EmitEarly falls back to a direct WriteFile and the notice is not lost. # the missing one, EmitEarly falls back to a direct WriteFile and the notice is not lost.
stdlib.free(t.CPtr(Pb)) stdlib.free(Pb)
CloseHandle(hFile) CloseHandle(hFile)
if _State.LineBuf == None: if _State.LineBuf == None:
EmitEarly("[traceback] WARN: LineBuf alloc failed, ctrl handler not installed\n") EmitEarly("[traceback] WARN: LineBuf alloc failed, ctrl handler not installed\n")
@@ -2049,7 +2045,7 @@ def Install() -> int:
# written too. Raw UTF-8 bytes, unaffected by the console code page; kept open for the # written too. Raw UTF-8 bytes, unaffected by the console code page; kept open for the
# whole process lifetime. # whole process lifetime.
string.strcpy(str(Pb + int(Plen)), ".stackdump.txt") string.strcpy(str(Pb + int(Plen)), ".stackdump.txt")
_DumpFile = wf.CreateFileA(Pb, TRACEBACK_FILE_APPEND_DATA, wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE, t.CPtr(0), wf.OPEN_ALWAYS, wf.FILE_ATTRIBUTE_NORMAL, t.CPtr(0)) _DumpFile = wf.CreateFileA(Pb, TRACEBACK_FILE_APPEND_DATA, wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE, None, wf.OPEN_ALWAYS, wf.FILE_ATTRIBUTE_NORMAL, None)
if _DumpFile == INVALID_HANDLE_VALUE: if _DumpFile == INVALID_HANDLE_VALUE:
_DumpFile = None _DumpFile = None
EmitText("[traceback] WARN: cannot open stack dump file, console-only output\n") EmitText("[traceback] WARN: cannot open stack dump file, console-only output\n")
@@ -2065,7 +2061,7 @@ def Install() -> int:
else: else:
LoadMapFile(hFile, int64(Sz.QuadPart)) LoadMapFile(hFile, int64(Sz.QuadPart))
CloseHandle(hFile) CloseHandle(hFile)
stdlib.free(t.CPtr(Pb)) stdlib.free(Pb)
# (5) dbghelp initialization (must happen here: Install runs at startup on the main thread, # (5) dbghelp initialization (must happen here: Install runs at startup on the main thread,
# while the Ctrl+C/Break callbacks run on new threads where dbghelp's module enumeration # while the Ctrl+C/Break callbacks run on new threads where dbghelp's module enumeration
# may be unavailable) + the two cross-module callback addresses, resolved dynamically. # may be unavailable) + the two cross-module callback addresses, resolved dynamically.
+2 -8
View File
@@ -1,7 +1,3 @@
import t, c
from stdint import *
class Buf: class Buf:
data: char* data: char*
length: sizet length: sizet
@@ -32,13 +28,11 @@ class Buf:
if self.length < self.capacity: self.data[self.length] = 0 if self.length < self.capacity: self.data[self.length] = 0
return n return n
def cstr(self) -> char*: def cstr(self) -> char*: return self.data
return self.data
def reset(self): def reset(self):
self.length = 0 self.length = 0
if self.data != None and self.capacity > 0: if self.data != None and self.capacity > 0: self.data[0] = 0
self.data[0] = 0
def __enter__(self) -> Buf*: return self def __enter__(self) -> Buf*: return self
def __exit__(self): self.reset() def __exit__(self): self.reset()
+4 -5
View File
@@ -1,6 +1,5 @@
import viperio import viperio
from stdarg import arg from stdarg import arg
import t
define TEMP_BUF_SIZE = 68 define TEMP_BUF_SIZE = 68
@@ -39,8 +38,8 @@ def _check_special_float(fv: double) -> int:
return 1 return 1
return 0 return 0
# The 4th parameter MUST stay named `args`: the printf wrappers forward their own va_list pointer # The 4th parameter MUST stay named `args`: the printf wrappers forward their own va_list pointer
# here (vprintf style), and the `arg()` sugar resolves the forwarded va_list by that name. # here (vprintf style), and the `arg()` sugar resolves the forwarded va_list by that name.
export def vsnprintf(buf: bytes, size: sizet, fmt: str, args: str) -> int: export def vsnprintf(buf: bytes, size: sizet, fmt: str, args: str) -> int:
if fmt is None: return 0 if fmt is None: return 0
# Length detection mode: when buf is None, only calculate the formatted length, without actually writing # Length detection mode: when buf is None, only calculate the formatted length, without actually writing
@@ -138,7 +137,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, args: str) -> int:
else: else:
ptr_i: int = arg() ptr_i: int = arg()
elif *fmt == 115: elif *fmt == 115:
s: str = arg(t.CPtr) s: str = arg(rawptr)
if s is None: s = "(None)" if s is None: s = "(None)"
slen: int = 0 slen: int = 0
sc: str = s sc: str = s
@@ -481,7 +480,7 @@ export def vsnprintf(buf: bytes, size: sizet, fmt: str, args: str) -> int:
ptr += _step ptr += _step
pad_count -= 1 pad_count -= 1
elif *fmt == 112: elif *fmt == 112:
ptr_val: uint64 = uint64(arg(t.CPtr)) ptr_val: uint64 = uint64(arg(rawptr))
if ptr_val == 0: if ptr_val == 0:
temp[0] = 40 temp[0] = 40
temp[1] = 110 temp[1] = 110
+1 -1
View File
@@ -623,7 +623,7 @@ vzeroupper""",
op=[ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm2', 'rcx', 'r8', 'r10']) op=[ASM_DESCR.CLOBBER_MEMORY, 'ymm0', 'ymm2', 'rcx', 'r8', 'r10'])
def simd_dot_array(a: t.CPtr, b: t.CPtr, n: sizet, out: t.CPtr): def simd_dot_array(a: double*, b: double*, n: sizet, out: double):
"""AVX2 batch: out[0] = dot(a[0:n], b[0:n]) using FMA accumulation""" """AVX2 batch: out[0] = dot(a[0:n], b[0:n]) using FMA accumulation"""
__asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)} __asm__(f"""mov rcx, {asminp(n, ASM_DESCR.REG_ANY)}
shr rcx, 2 shr rcx, 2
+3 -3
View File
@@ -13,12 +13,12 @@ from rwlock import RWLock
class Thread: class Thread:
"""Thread class, wraps Windows CreateThread. """Thread class, wraps Windows CreateThread.
Thread callback signature: def thread_func(arg: t.CPtr) -> t.CPtr Thread callback signature: def thread_func(arg: rawptr) -> rawptr
Note: Windows API limits the return value to DWORD (32-bit), so 64-bit pointers will be truncated. Note: Windows API limits the return value to DWORD (32-bit), so 64-bit pointers will be truncated.
If you need to return a 64-bit result, pass it via the arg parameter struct. If you need to return a 64-bit result, pass it via the arg parameter struct.
Usage: Usage:
def worker(arg: t.CPtr) -> t.CPtr: def worker(arg: rawptr) -> rawptr:
# thread logic # thread logic
return NULL return NULL
@@ -84,7 +84,7 @@ class local:
Usage: Usage:
tls: local = local() tls: local = local()
tls.set(value_ptr) tls.set(value_ptr)
val: t.CPtr = tls.get() val: rawptr = tls.get()
""" """
index: ULONG index: ULONG