perf(otf): buildSubsetGids 用 gidToNewGid Map 替代 indexOf O(n) 查找

subsetOTF 的 buildSubsetGids 原 `cpToNewGid.set(cp, subsetGids.indexOf(gid))`
对每个 codepoint 线性遍历 subsetGids 找索引,O(N×M),全字符集场景退化为 O(N²)。

改用 gidToNewGid Map(gid→新gid)在 push 时记录,O(1) 查找;同时替代 seenGid
Set(gidToNewGid.has 等价)。

基准测试 OTF 用例(小字集)SSIM 全一致、输出字节 IDENTICAL(otf-五个汉字
26536/15024、otf-思源 19188/10664、otf-白狐千字文 35896)。算法缺陷修复,
大字集 OTF 子集化(完整 CJK OTF)有实质收益。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-25 08:23:01 +08:00
parent 71105374ee
commit 7b3b0ef592

View File

@ -142,7 +142,9 @@ function buildSubsetGids(
const { fmt4Off, fmt12Off } = selectCmapSubtables(dv, cmapOff);
/** origGid 集合(保持插入顺序,新 gid = 数组 index */
const subsetGids: number[] = [0];
const seenGid = new Set<number>([0]);
/** gid gidsubsetGids subsetGids.indexOf(gid) O(n) 线
* indexOf codepoint subsetGidsO(N×M)退 O(N²) */
const gidToNewGid = new Map<number, number>([[0, 0]]);
const cpToNewGid = new Map<number, number>();
for (const cp of codePoints) {
if (cpToNewGid.has(cp)) continue;
@ -150,11 +152,13 @@ function buildSubsetGids(
if (cp < 0x10000 && fmt4Off >= 0) gid = lookupFormat4(dv, fmt4Off, cp);
if (gid === 0 && fmt12Off >= 0) gid = lookupFormat12(dv, fmt12Off, cp);
if (gid === 0) continue; /** 字体无此字 */
if (!seenGid.has(gid)) {
seenGid.add(gid);
let newGid = gidToNewGid.get(gid);
if (newGid === undefined) {
newGid = subsetGids.length;
subsetGids.push(gid);
gidToNewGid.set(gid, newGid);
}
cpToNewGid.set(cp, subsetGids.indexOf(gid));
cpToNewGid.set(cp, newGid);
}
return { subsetGids, cpToNewGid };
}