perf(gen): 结构体/类型查找去线性化并守卫全量遍历残量
StructGenMixin: 加「名 | Ty 指针值 -> 首注册下标」索引,find_struct/find_struct_by_module/find_struct_by_type 走 O(1) 快速路径;每次注册占槽保序,中和/改名后键不等自然跳过,未命中或索引不可信回退原扫描。并补 _struct_name_hash/_struct_ty_hash(32 位 FNV,常量均小于 2^63 以避开大十进制字面量解析风险)。 TypeSpec.InternSpec: 加「文本键 -> 驻留下标」开放寻址索引,去重由 O(n) 降为期望 O(1);命中返回原同一实例,注册顺序/容量 fail-fast 语义不变。 register_struct/lookup_field/get_class_name_by_type 的按 Ty 指针首匹配改走 find_struct_by_type;CallGenLeafKit/CallGenCall 的按序号全量遍历补守卫注释(避免后续误改为按名查找)。
This commit is contained in:
1 parent
0dd7511a9b
commit
76df3f0225
4 files changed
+387
-47
No files matched your search
@@ -846,6 +846,10 @@ class CallGenCall(CallGenBase.CallGenBase):
|
||||
if sib_recv_local != 0:
|
||||
sc_i: int = 0
|
||||
sc_n: int = self.Gen.StructGen.get_struct_count()
|
||||
# 【守卫】这是按序号(0..StructCount-1)的全量遍历,非按名查找,
|
||||
# 不走 StructNameIdx 索引:兄弟 Mixin 候选是"名字不等 class_name
|
||||
# 且本模块存在同名方法"的任意条目,需逐条枚举探测,不可改为按名
|
||||
# find_struct(会退化为单条精确查找、漏掉全部兄弟候选)。
|
||||
while sc_i < sc_n and found_func is None and mixin_hit == 0:
|
||||
cand_entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen._get_struct_entry(sc_i)
|
||||
if cand_entry is not None and cand_entry.Name is not None:
|
||||
|
||||
@@ -393,6 +393,9 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
names[outcount] = sizet(cur)
|
||||
outcount += 1
|
||||
# 子类入队:遍历全部已注册 struct,ParentNames 含 cur 者
|
||||
# 【守卫】这是按序号(0..StructCount-1)的全量遍历,非按名查找,
|
||||
# 不走 StructNameIdx 索引:需枚举每个条目的 ParentNames | 按序号
|
||||
# 取条目,语义等价于"扫描全表收集子类",不可改为 find_struct。
|
||||
scnt: int = self.Gen.StructGen.get_struct_count()
|
||||
for i in range(scnt):
|
||||
e2: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen._get_struct_entry(i)
|
||||
@@ -584,23 +587,18 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
|
||||
def _ensure_closure_names(self) -> int:
|
||||
"""确保闭包名表已分配,0 失败"""
|
||||
if self.ClosureNames is not None:
|
||||
return 1
|
||||
if self.ClosureNames is not None: return 1
|
||||
self.ClosureNames = CompilerFactory.factory.PermPool.alloc(64 * MAX_CLOSURE_NAMES)
|
||||
if self.ClosureNames is None:
|
||||
return 0
|
||||
if self.ClosureNames is None: return 0
|
||||
string.memset(self.ClosureNames, 0, 64 * MAX_CLOSURE_NAMES)
|
||||
return 1
|
||||
|
||||
|
||||
def register_closure_name(self, name: str) -> int:
|
||||
"""注册嵌套函数(闭包变量)名,0 成功"""
|
||||
if name is None:
|
||||
return 1
|
||||
if self._ensure_closure_names() == 0:
|
||||
return 1
|
||||
if self.ClosureCount >= MAX_CLOSURE_NAMES:
|
||||
return 1
|
||||
if name is None: return 1
|
||||
if self._ensure_closure_names() == 0: return 1
|
||||
if self.ClosureCount >= MAX_CLOSURE_NAMES: return 1
|
||||
idx: sizet = sizet(self.ClosureCount) * 64
|
||||
string.strncpy(self.ClosureNames + idx, name, 63)
|
||||
self.ClosureCount += 1
|
||||
|
||||
@@ -33,6 +33,8 @@ import lib.core.Gen.GenApi as GenApi
|
||||
# 常量(与 HandlesStruct 保持一致)
|
||||
# ============================================================
|
||||
STRUCT_MAX: t.CDefine = 512
|
||||
# 名 | 类型 下标索引槽数(2 的幂且 > 2*STRUCT_MAX ⇒ 负载 < 0.5,探测必然终止)
|
||||
STRUCT_IDX_CAP: t.CDefine = 1024
|
||||
# 字段槽位上限制(与 HandlesStruct 保持一致;Generator 43 Mixin 展平 ~120 字段,
|
||||
# 2026-08-16 追加78:64 截断导致第 9+ 父类字段(StmtGenMixin.DiagStmtCount 等)
|
||||
# 丢失 → __init__ 写 self.DiagStmtCount "属性写入失败")
|
||||
@@ -43,6 +45,43 @@ FIELD_NAME_MAX: t.CDefine = 64
|
||||
PARENT_MAX: t.CDefine = 64
|
||||
MRO_MAX: t.CDefine = 64
|
||||
|
||||
# 索引哈希常量(32 位 FNV-1a,常量均 < 2^63 以避开大十进制字面量解析风险)
|
||||
FNV32_OFFSET: t.CDefine = 2166136261
|
||||
FNV32_PRIME: t.CDefine = 16777619
|
||||
FNV32_MASK: t.CDefine = 4294967295
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 索引哈希函数(名 | Ty 指针值 → 64 位哈希)
|
||||
# 供 StructTable 的「名 | Ty 指针值 → 首注册下标」索引探测使用
|
||||
# (见 _struct_name_idx_insert / _struct_name_idx_find 等)。
|
||||
# ============================================================
|
||||
def _struct_name_hash(name: str) -> uint64:
|
||||
"""名字 → 哈希(32 位 FNV-1a,逐字节到 NUL 结束;空名返回偏移基值)"""
|
||||
h: uint64 = uint64(FNV32_OFFSET)
|
||||
if name is None:
|
||||
return h
|
||||
i: int = 0
|
||||
while name[i] != '\0':
|
||||
h = (h ^ uint64(int(name[i]))) * uint64(FNV32_PRIME)
|
||||
h = h & uint64(FNV32_MASK)
|
||||
i += 1
|
||||
return h
|
||||
|
||||
|
||||
def _struct_ty_hash(tyval: uint64) -> uint64:
|
||||
"""Ty 指针值 → 哈希(对其 8 字节做 32 位 FNV-1a)"""
|
||||
h: uint64 = uint64(FNV32_OFFSET)
|
||||
v: uint64 = tyval
|
||||
j: int = 0
|
||||
while j < 8:
|
||||
h = (h ^ (v & uint64(255))) * uint64(FNV32_PRIME)
|
||||
h = h & uint64(FNV32_MASK)
|
||||
v = v >> 8
|
||||
j += 1
|
||||
return h
|
||||
|
||||
|
||||
|
||||
# ============================================================
|
||||
# StructGenMixin - 结构体注册表/字段查找/可见性
|
||||
@@ -63,6 +102,12 @@ class StructGenMixin:
|
||||
VisibleStructs: hashtable.HashTable | t.CPtr
|
||||
StrictVisibility: int
|
||||
|
||||
# --- 名 | 类型 下标索引(查找快速路径;索引不可信时回退原线性扫描) ---
|
||||
StructNameIdx: sizet | t.CPtr # 名 → 首注册下标(槽 u64:0=空槽,否则 index+1)
|
||||
StructTypeIdx: sizet | t.CPtr # Ty 指针值 → 首注册下标(同布局)
|
||||
StructIdxCap: int # 索引槽数(2 的幂,> 2*STRUCT_MAX)
|
||||
StructIdxBuilt: int # 已同步建索引条目数(-1=不可信;= StructCount 才可信)
|
||||
|
||||
def InitStructGen(self) -> int:
|
||||
"""初始化结构体 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
|
||||
if self is None:
|
||||
@@ -72,6 +117,10 @@ class StructGenMixin:
|
||||
self.StdlibAlloc = 0
|
||||
self.VisibleStructs = None
|
||||
self.StrictVisibility = 0
|
||||
self.StructNameIdx = None
|
||||
self.StructTypeIdx = None
|
||||
self.StructIdxCap = 0
|
||||
self.StructIdxBuilt = 0
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
@@ -145,6 +194,24 @@ class StructGenMixin:
|
||||
return 0
|
||||
string.memset(self.StructTable, 0, entry_size * STRUCT_MAX)
|
||||
self.StructCount = 0
|
||||
# 建 名 | 类型 索引(与表同池分配:表存活则索引存活,生命周期一致)。
|
||||
# 索引是可选加速层:分配失败则保持 None 且 StructIdxBuilt=-1,查找自动
|
||||
# 回退原线性扫描(语义完全一致),故不因此使初始化失败。
|
||||
idx_bytes: sizet = sizet(STRUCT_IDX_CAP) * 8
|
||||
name_idx: sizet | t.CPtr = table_pool.alloc(idx_bytes)
|
||||
type_idx: sizet | t.CPtr = table_pool.alloc(idx_bytes)
|
||||
if name_idx is None or type_idx is None:
|
||||
self.StructNameIdx = None
|
||||
self.StructTypeIdx = None
|
||||
self.StructIdxCap = 0
|
||||
self.StructIdxBuilt = -1
|
||||
else:
|
||||
string.memset(name_idx, 0, idx_bytes)
|
||||
string.memset(type_idx, 0, idx_bytes)
|
||||
self.StructNameIdx = name_idx
|
||||
self.StructTypeIdx = type_idx
|
||||
self.StructIdxCap = STRUCT_IDX_CAP
|
||||
self.StructIdxBuilt = 0
|
||||
return 1
|
||||
|
||||
def _get_struct_entry(self, i: int) -> GenTypes.StructEntry | t.CPtr:
|
||||
@@ -155,6 +222,154 @@ class StructGenMixin:
|
||||
addr: uint64 = uint64(self.StructTable) + i * entry_size
|
||||
return (GenTypes.StructEntry | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||||
|
||||
# ============================================================
|
||||
# 名 | 类型 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽)
|
||||
#
|
||||
# 目的:把 find_struct/find_struct_by_module(名)与 find_struct_by_type
|
||||
# (类型指针)从 O(n) 全表扫描降为期望 O(1)。索引在注册处同步维护;
|
||||
# 冲突逐键比较(名用 strcmp,类型用 Ty 指针值),不能只比哈希。
|
||||
# 索引"可信"条件 = StructIdxBuilt == StructCount(每次注册后同步);
|
||||
# 不可信/不可用时查找一律回退原线性扫描,保证语义逐字节一致。
|
||||
# 条目被中和(Name[0]=0 / Ty=None,见 ClassGenDef/ClassGenEnum)后索引槽成为
|
||||
# 失效键:查找探测时键不等 → 继续探测/判定未命中,不会返回失效条目。
|
||||
# ============================================================
|
||||
def _struct_entry_name(self, i: int) -> str:
|
||||
"""取第 i 个条目的 Name(供索引探测比较),越界/空返回 None"""
|
||||
e: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if e is None:
|
||||
return None
|
||||
return e.Name
|
||||
|
||||
def _struct_entry_tyval(self, i: int) -> uint64:
|
||||
"""取第 i 个条目的 Ty 指针值(Ty=None 返回 0)"""
|
||||
e: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if e is None or e.Ty is None:
|
||||
return 0
|
||||
return uint64(e.Ty)
|
||||
|
||||
def _struct_name_idx_insert(self, ei: int, name: str) -> int:
|
||||
"""把 (name → ei) 写入名索引;返回 0=成功 / 1=失败
|
||||
|
||||
每次注册都占用一个槽(不按键去重):同键的多个条目按注册顺序沿探测链
|
||||
递增排列,查找取链上首个"当前键相等"者即 = 原扫描的首个命中。若按键去重,
|
||||
则最早的重复项被中和(Name[0]=0)后,被丢弃的后续重复项将无法找回。
|
||||
"""
|
||||
base: sizet | t.CPtr = self.StructNameIdx
|
||||
if base is None or self.StructIdxCap <= 0:
|
||||
return 1
|
||||
h: uint64 = _struct_name_hash(name)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
i: int = 0
|
||||
while i < self.StructIdxCap:
|
||||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(ei + 1)
|
||||
return 0
|
||||
idx = (idx + 1) & mask
|
||||
i += 1
|
||||
return 1
|
||||
|
||||
def _struct_type_idx_insert(self, ei: int, tyval: uint64) -> int:
|
||||
"""把 (Ty 指针值 → ei) 写入类型索引;0=成功 / 1=失败
|
||||
|
||||
与名索引同策略:每次注册都占槽(不去重),保证探测链上同键条目按注册
|
||||
顺序排列,查找首个"当前 Ty 相等"者 = 原扫描首个命中。
|
||||
"""
|
||||
base: sizet | t.CPtr = self.StructTypeIdx
|
||||
if base is None or self.StructIdxCap <= 0 or tyval == 0:
|
||||
return 1
|
||||
h: uint64 = _struct_ty_hash(tyval)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
i: int = 0
|
||||
while i < self.StructIdxCap:
|
||||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(ei + 1)
|
||||
return 0
|
||||
idx = (idx + 1) & mask
|
||||
i += 1
|
||||
return 1
|
||||
|
||||
def _struct_idx_add(self, ei: int, entry: GenTypes.StructEntry | t.CPtr) -> None:
|
||||
"""新增条目后同步两张索引;任一写入失败 → 标记索引不可信(查找回退扫描)"""
|
||||
if self.StructNameIdx is None or self.StructTypeIdx is None or self.StructIdxCap <= 0:
|
||||
self.StructIdxBuilt = -1
|
||||
return
|
||||
failed: int = 0
|
||||
if entry is not None and entry.Name is not None:
|
||||
failed = failed + self._struct_name_idx_insert(ei, entry.Name)
|
||||
if entry is not None and entry.Ty is not None:
|
||||
failed = failed + self._struct_type_idx_insert(ei, uint64(entry.Ty))
|
||||
if failed != 0:
|
||||
self.StructIdxBuilt = -1
|
||||
else:
|
||||
# ei = StructCount-1,故同步后已建索引条目数 = StructCount
|
||||
self.StructIdxBuilt = self.StructCount
|
||||
|
||||
def _struct_name_idx_find(self, name: str) -> int:
|
||||
"""名索引查首注册下标:>=0 命中(= 原扫描首个同名条目下标);否则 -1
|
||||
|
||||
-1 表示"未命中或索引不可用",调用方须回退原线性扫描(保证正确性)。
|
||||
探测链上同键条目按注册顺序排列,故首个"当前键相等"者即原扫描首个命中。
|
||||
"""
|
||||
base: sizet | t.CPtr = self.StructNameIdx
|
||||
if base is None or self.StructIdxCap <= 0 or self.StructIdxBuilt != self.StructCount:
|
||||
return -1
|
||||
# 空名("")特例:被中和的条目 Name 恰为 ""(Name[0]=0),原扫描会命中首个
|
||||
# 空名条目;索引槽按"注册时的原名"哈希放置,无法保证给出首个空名条目 →
|
||||
# 空名一律回退原扫描,保证逐字节一致。
|
||||
if name is None or name[0] == '\0':
|
||||
return -1
|
||||
h: uint64 = _struct_name_hash(name)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
i: int = 0
|
||||
while i < self.StructIdxCap:
|
||||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0:
|
||||
return -1
|
||||
se: int = int(sv) - 1
|
||||
ek: str = self._struct_entry_name(se)
|
||||
if ek is not None and string.strcmp(ek, name) == 0:
|
||||
return se
|
||||
idx = (idx + 1) & mask
|
||||
i += 1
|
||||
return -1
|
||||
|
||||
def _struct_type_idx_find(self, tyval: uint64) -> int:
|
||||
"""类型索引查首注册下标:>=0 命中;-1 未命中/不可信(须回退扫描)
|
||||
|
||||
类型索引不保证覆盖全部 Ty 值(如 ClassGenDef 深拷贝后 existing.Ty 被
|
||||
改写为新指针,未入索引)⇒ 未命中不能断言"确无",一律回退线性扫描。
|
||||
"""
|
||||
base: sizet | t.CPtr = self.StructTypeIdx
|
||||
if base is None or self.StructIdxCap <= 0 or tyval == 0:
|
||||
return -1
|
||||
if self.StructIdxBuilt != self.StructCount:
|
||||
return -1
|
||||
h: uint64 = _struct_ty_hash(tyval)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
i: int = 0
|
||||
while i < self.StructIdxCap:
|
||||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(base) + uint64(idx * 8), t.CPtr))
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0:
|
||||
return -1
|
||||
se: int = int(sv) - 1
|
||||
if self._struct_entry_tyval(se) == tyval:
|
||||
return se
|
||||
idx = (idx + 1) & mask
|
||||
i += 1
|
||||
return -1
|
||||
|
||||
def _get_field_entry(self, struct_entry: GenTypes.StructEntry | t.CPtr, i: int) -> GenTypes.FieldEntry | t.CPtr:
|
||||
"""获取结构体中第 i 个字段条目"""
|
||||
if struct_entry is None or i < 0 or i >= FIELD_MAX:
|
||||
@@ -178,12 +393,13 @@ class StructGenMixin:
|
||||
|
||||
# 用类型指针去重(同一类型不重复注册,支持跨模块同名类)
|
||||
# 用 uint64 指针值比较(is 比较会触发类型安全检查解引用悬空 Ty)
|
||||
st_dedup: uint64 = uint64(struct_ty)
|
||||
for i in range(self.StructCount):
|
||||
existing: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if existing is not None and existing.Ty is not None:
|
||||
if uint64(existing.Ty) == st_dedup:
|
||||
return existing
|
||||
# 类型索引快速路径:find_struct_by_type 的 Ty 指针身份语义 = 原去重扫描
|
||||
# "首个 Ty 指针相等条目"(首注册优先),且其索引未命中 | 不可信时会回退
|
||||
# 原线性扫描(正确性保证,非兜底)⇒ 与原扫描结果逐字节一致。
|
||||
# 注册途中索引恒与 StructCount 同步(每次注册后 _struct_idx_add 同步)。
|
||||
existing: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||||
if existing is not None:
|
||||
return existing
|
||||
|
||||
if self.StructCount >= STRUCT_MAX:
|
||||
VLogger.error(f"table full, cannot register {name}", "STRUCT")
|
||||
@@ -234,6 +450,8 @@ class StructGenMixin:
|
||||
entry.ModuleSha1 = sha1_buf
|
||||
|
||||
self.StructCount += 1
|
||||
# 同步名 | 类型索引(新条目下标 = StructCount-1)
|
||||
self._struct_idx_add(self.StructCount - 1, entry)
|
||||
return entry
|
||||
|
||||
def register_struct_alias(self, pool: rowptr[memhub.MemBuddy],
|
||||
@@ -302,6 +520,8 @@ class StructGenMixin:
|
||||
entry.MethodsEmitted = 0
|
||||
entry.SpecTriggerSha1 = None
|
||||
self.StructCount += 1
|
||||
# 同步名 | 类型索引(别名条目与源条目同名 | 同 Ty,索引保留首注册下标)
|
||||
self._struct_idx_add(self.StructCount - 1, entry)
|
||||
return entry
|
||||
|
||||
def set_struct_sha1(self, pool: rowptr[memhub.MemBuddy],
|
||||
@@ -565,6 +785,12 @@ class StructGenMixin:
|
||||
if self.StructCount < 0 or self.StructCount > 200000:
|
||||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct)", "STRUCT")
|
||||
stdlib.exit(1)
|
||||
# 名索引快速路径:命中即首注册下标(语义 = 原精确匹配扫描首个命中)。
|
||||
# 未命中/索引不可信 → 回退下方原精确匹配扫描(保证正确性);
|
||||
# 精确匹配确无时继续走模块限定名/裸名回退逻辑(不提前 return None)。
|
||||
fi: int = self._struct_name_idx_find(name)
|
||||
if fi >= 0:
|
||||
return self._get_struct_entry(fi)
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
@@ -657,7 +883,14 @@ class StructGenMixin:
|
||||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_module)", "STRUCT")
|
||||
stdlib.exit(1)
|
||||
if sha1 is not None:
|
||||
for i in range(self.StructCount):
|
||||
# 名索引快速路径:同名条目都位于 >= 首个同名下标处,故可从该下标起扫描
|
||||
# name+sha1(等价原全表扫描"首个 name 且 sha1 命中")。
|
||||
# 未命中/索引不可信 ⇒ start=0,走原全表扫描(保证正确性)。
|
||||
start: int = 0
|
||||
fri: int = self._struct_name_idx_find(name)
|
||||
if fri >= 0:
|
||||
start = fri
|
||||
for i in range(start, self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0:
|
||||
@@ -723,6 +956,12 @@ class StructGenMixin:
|
||||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_type)", "STRUCT")
|
||||
stdlib.exit(1)
|
||||
st_val: uint64 = uint64(struct_ty)
|
||||
# 类型索引快速路径:命中即首注册下标(语义 = 原扫描首个 Ty 指针相等条目)。
|
||||
# 未命中/索引不可信 → 回退原线性扫描(Ty 可能被深拷贝改写,索引未覆盖全部
|
||||
# Ty 值,故此处未命中不能断言"确无",必须回退)。
|
||||
ti: int = self._struct_type_idx_find(st_val)
|
||||
if ti >= 0:
|
||||
return self._get_struct_entry(ti)
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Ty is not None:
|
||||
@@ -774,19 +1013,18 @@ class StructGenMixin:
|
||||
# ============================================================
|
||||
|
||||
# 第一遍: is 身份比较(用 uint64 指针值比较,避免 is 编译路径解引用悬空 Ty)
|
||||
st_lk: uint64 = uint64(struct_ty)
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Ty is not None:
|
||||
if uint64(entry.Ty) == st_lk:
|
||||
# is 匹配,查找字段
|
||||
for fi in range(entry.FieldCount):
|
||||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
if string.strcmp(fe.Name, field_name) == 0:
|
||||
return fe
|
||||
# is 匹配但字段未找到(类型唯一确定,无需继续)
|
||||
return None
|
||||
# 类型索引快速路径:find_struct_by_type 的语义 = 原扫描"首个 Ty 指针相等
|
||||
# 条目"(首注册优先);索引未命中 | 不可信时其内部回退原线性扫描
|
||||
# (正确性保证,非兜底)⇒ 选中的 entry 与原 is 身份扫描完全一致。
|
||||
# 类型唯一确定 entry:命中后查字段,字段未找到直接返回 None(不继续遍历)。
|
||||
lk_entry: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||||
if lk_entry is not None:
|
||||
for fi in range(lk_entry.FieldCount):
|
||||
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(lk_entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
if string.strcmp(fe.Name, field_name) == 0:
|
||||
return fe
|
||||
return None
|
||||
|
||||
# 第二遍: 名称回退(is 未匹配到)
|
||||
ty_name: str = self._extract_struct_name(struct_ty)
|
||||
@@ -1262,12 +1500,12 @@ class StructGenMixin:
|
||||
"""按类型指针查找类名,返回类名字符串或 None"""
|
||||
if struct_ty is None or self.StructTable is None:
|
||||
return None
|
||||
st_cn: uint64 = uint64(struct_ty)
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Ty is not None:
|
||||
if uint64(entry.Ty) == st_cn:
|
||||
return entry.Name
|
||||
# 类型索引快速路径:find_struct_by_type 的 Ty 指针身份语义 = 原扫描
|
||||
# "首个 Ty 指针相等条目"(首注册优先),返回其 Name;索引未命中 |
|
||||
# 不可信时其内部回退原线性扫描(正确性保证,非兜底)⇒ 逐字节一致。
|
||||
fast_cn: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||||
if fast_cn is not None:
|
||||
return fast_cn.Name
|
||||
# 回退: 类型指针比较失败时,遍历所有注册的结构体按名称匹配
|
||||
# 提取 struct_ty 的 Name 字段(可能是 "SHA1.ClassName" 格式)
|
||||
ty_name: str = self._extract_struct_name(struct_ty)
|
||||
|
||||
@@ -281,6 +281,27 @@ class TypeSpecArena:
|
||||
Specs: sizet | t.CPtr
|
||||
Count: int
|
||||
Cap: int
|
||||
# 规范文本键 → 驻留下标 的开放寻址哈希索引(槽为 u64:0=空槽,否则 index+1)。
|
||||
# 可选加速层:为 None 时 InternSpec 回退原线性扫描(语义完全一致)。
|
||||
Index: sizet | t.CPtr
|
||||
# 索引槽数(2 的幂,> 2*Cap;仅 Index 非 None 时有效)
|
||||
IndexCap: int
|
||||
|
||||
|
||||
# 驻留哈希索引 FNV-1a 64 位常量(与 hashtable.vp 同款)
|
||||
_SPEC_FNV_OFFSET: t.CUInt64T = 0xcbf29ce484222325
|
||||
_SPEC_FNV_PRIME: t.CUInt64T = 0x100000001b3
|
||||
|
||||
|
||||
def _spec_key_hash(key: str) -> uint64:
|
||||
"""规范文本键的 FNV-1a 64 位哈希(同一键必得同一值;仅用于槽定位)"""
|
||||
h: t.CUInt64T = _SPEC_FNV_OFFSET
|
||||
k: str = key
|
||||
while k[0] != 0:
|
||||
h = h ^ t.CUInt64T(k[0])
|
||||
h = h * _SPEC_FNV_PRIME
|
||||
k += 1
|
||||
return uint64(h)
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -294,6 +315,56 @@ def _spec_key_at(keys: bytes, index: int) -> str:
|
||||
return (str | t.CPtr)(t.CVoid(addr, t.CPtr))
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _spec_index_find - 在驻留哈希索引中查找键,返回条目下标(未命中/不可用返回 -1)
|
||||
# 槽存 index+1(0=空槽);线性探测,逐槽比较键文本——必须按键内容区分,
|
||||
# 不能只比哈希(否则冲突键会误命中并返回错误实例)。
|
||||
# ============================================================
|
||||
def _spec_index_find(arena: TypeSpecArena | t.CPtr, key: str, h: uint64) -> int:
|
||||
"""查驻留哈希索引,命中返回条目下标;未命中返回 -1"""
|
||||
if arena.Index is None or arena.IndexCap <= 0:
|
||||
return -1
|
||||
mask: sizet = sizet(arena.IndexCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
i: int = 0
|
||||
while i < arena.IndexCap:
|
||||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(arena.Index) + uint64(idx * 8), t.CPtr))
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0:
|
||||
return -1
|
||||
ei: int = int(sv) - 1
|
||||
ek: str = _spec_key_at(arena.Keys, ei)
|
||||
if ek is not None and string.strcmp(ek, key) == 0:
|
||||
return ei
|
||||
idx = (idx + 1) & mask
|
||||
i += 1
|
||||
return -1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _spec_index_insert - 把条目 ei 写入驻留哈希索引
|
||||
# 返回 0=成功 / 1=索引不可用或已满。仅在追加新条目后调用
|
||||
# (条目只增不删,故无需墓碑;槽数 > 2*Cap ⇒ 必然存在空槽,探测终止)。
|
||||
# ============================================================
|
||||
def _spec_index_insert(arena: TypeSpecArena | t.CPtr, key: str, h: uint64, ei: int) -> int:
|
||||
"""把下标 ei 写入驻留哈希索引,返回 0=成功 / 1=不可用"""
|
||||
if arena.Index is None or arena.IndexCap <= 0:
|
||||
return 1
|
||||
mask: sizet = sizet(arena.IndexCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
i: int = 0
|
||||
while i < arena.IndexCap:
|
||||
slot: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(arena.Index) + uint64(idx * 8), t.CPtr))
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(ei + 1)
|
||||
return 0
|
||||
idx = (idx + 1) & mask
|
||||
i += 1
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SpecArenaInit - 初始化驻留表(幂等;cap<=0 时用 SPEC_INTERN_MAX)
|
||||
# 返回 0=成功 / 1=失败(分配失败 fail-fast 由调用方处理,此处返回错误码)
|
||||
@@ -320,6 +391,20 @@ def SpecArenaInit(arena: TypeSpecArena | t.CPtr, cap: int) -> int:
|
||||
arena.Specs = specs
|
||||
arena.Count = 0
|
||||
arena.Cap = use_cap
|
||||
# 建驻留哈希索引(槽数取 2 的幂且 > 2*Cap ⇒ 负载 < 0.5)。索引是可选加速层:
|
||||
# 分配失败则保持 None,InternSpec 自动回退线性扫描(语义完全一致),
|
||||
# 故不因索引失败而使整个初始化失败(不新增失败面)。
|
||||
icap: int = 1
|
||||
while icap < use_cap * 2:
|
||||
icap = icap * 2
|
||||
idx_buf: sizet | t.CPtr = pp.alloc(icap * 8)
|
||||
if idx_buf is not None:
|
||||
string.memset(idx_buf, 0, icap * 8)
|
||||
arena.Index = idx_buf
|
||||
arena.IndexCap = icap
|
||||
else:
|
||||
arena.Index = None
|
||||
arena.IndexCap = 0
|
||||
return 0
|
||||
|
||||
|
||||
@@ -831,17 +916,29 @@ def InternSpec(arena: TypeSpecArena | t.CPtr,
|
||||
# 关键在于**绝不能把截断键写入表**:截断键会让后续 strcmp 撞上别的类型
|
||||
# 并返回**错误实例**(2026-09-11 实测的格式化闸门误报根因)。
|
||||
return sp
|
||||
# 线性查找已有键(规模与类型种数同阶,量级不大;命中即复用)
|
||||
i: int = 0
|
||||
while i < arena.Count:
|
||||
exist_key: str = _spec_key_at(arena.Keys, i)
|
||||
if exist_key is not None and string.strcmp(exist_key, key_buf) == 0:
|
||||
slot_e: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(arena.Specs) + uint64(i * 8), t.CPtr))
|
||||
if slot_e is None:
|
||||
return None
|
||||
return (TypeSpec | t.CPtr)(t.CVoid(uint64(slot_e[0]), t.CPtr))
|
||||
i += 1
|
||||
# 哈希索引快速路径:期望 O(1)。索引在下方追加处同步维护(条目只增不删)⇒
|
||||
# 索引"完整":未命中即可直接追加,无需回退线性扫描。仅当索引不可用(分配失败/
|
||||
# 旧表未建)时回退原线性扫描 —— 这是保证正确性的回退,非静默兜底。
|
||||
key_hash: uint64 = _spec_key_hash(key_buf)
|
||||
found: int = _spec_index_find(arena, key_buf, key_hash)
|
||||
if found >= 0:
|
||||
slot_e: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(arena.Specs) + uint64(found * 8), t.CPtr))
|
||||
if slot_e is None:
|
||||
return None
|
||||
return (TypeSpec | t.CPtr)(t.CVoid(uint64(slot_e[0]), t.CPtr))
|
||||
if arena.Index is None:
|
||||
# 索引不可用:回退原线性查找(规模与类型种数同阶)
|
||||
i: int = 0
|
||||
while i < arena.Count:
|
||||
exist_key: str = _spec_key_at(arena.Keys, i)
|
||||
if exist_key is not None and string.strcmp(exist_key, key_buf) == 0:
|
||||
slot_e2: sizet | t.CPtr = (sizet | t.CPtr)(
|
||||
t.CVoid(uint64(arena.Specs) + uint64(i * 8), t.CPtr))
|
||||
if slot_e2 is None:
|
||||
return None
|
||||
return (TypeSpec | t.CPtr)(t.CVoid(uint64(slot_e2[0]), t.CPtr))
|
||||
i += 1
|
||||
# 未命中:追加。**表满 = fail-fast**(Debugger/_Type.md D14 风险 2):
|
||||
# 不能返回 None —— 调用链会把 None 一路传到 entry.Spec,条目**静默失去类型信息**
|
||||
# (如 _entry_spec_unsigned 恒返回 0 ⇒ zext/sext 方向取错),属静默数据损坏,
|
||||
@@ -863,6 +960,9 @@ def InternSpec(arena: TypeSpecArena | t.CPtr,
|
||||
if slot_n is None:
|
||||
return None
|
||||
slot_n[0] = sizet(sp)
|
||||
# 同步写入哈希索引(下标 = 追加前的 Count)。插入失败仅意味着后续查找对该键
|
||||
# 回退线性扫描,不影响正确性。
|
||||
_spec_index_insert(arena, key_buf, key_hash, arena.Count)
|
||||
arena.Count += 1
|
||||
return sp
|
||||
|
||||
|
||||
Reference in new issue
Block a user