From 6c59c3e7e6df98a30c95943a6168ef6061ebceb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=AE=E7=94=9F=EF=BC=88=E5=AD=90=E8=99=9A=EF=BC=89?= <2234839456@qq.com> Date: Fri, 24 Jul 2026 17:58:15 +0800 Subject: [PATCH] =?UTF-8?q?perf(gsub-subset):=20parseChainRuleFormat1or2?= =?UTF-8?q?=20=E8=BE=B9=E8=AF=BB=E8=BE=B9=20remap=20=E6=8F=90=E5=89=8D?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原实现对 back/input/look 先读到 backRaw/inputRaw/lookRaw 三个临时数组, 再二次遍历 remapSeq 重映射,format1 含子集外 gid 时规则失效(return null), 但 raw 数组已分配——FiraCode 多数 fmt1 规则因 input gid 不在子集而失效,raw 分配纯浪费。 改为 readSeq 边读边重映射(format1):遇子集外 gid 立即返回 null,省掉三个 临时数组与二次遍历。format2(class index 始终有效)原样读取不重映射,行为不变。 subsetGSUB min(checksum 与改动前完全一致): - FiraCode 0.296→0.290ms(-2%) - 初夏纯标点 0.075→0.071ms(-5%) 全字体 SSIM/输出字节均不变。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/gsub-subset.ts | 49 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/backend/font_util/gsub-subset.ts b/backend/font_util/gsub-subset.ts index f88c1ea..d18fe26 100644 --- a/backend/font_util/gsub-subset.ts +++ b/backend/font_util/gsub-subset.ts @@ -837,22 +837,42 @@ function parseChainRuleFormat1or2( /** count 异常大(偏移错位读到垃圾值)则放弃该规则,返回 null。实际规则序列长度很小(<256) */ if (backCount > 255) return null; let p = ruleOff + 2; - const backRaw: number[] = []; - for (let k = 0; k < backCount; k++) backRaw.push(r.u16(p + k * 2)); + /** + * format1(gid 序列):边读边重映射,遇子集外 gid 立即返回 null——避免先收集 backRaw/inputRaw/lookRaw + * 三个临时数组再二次遍历 remap(子集外规则的 raw 数组分配纯浪费,FiraCode 多数 fmt1 规则因 input gid + * 不在子集而失效)。format2(class index):始终有效,原样读取不重映射。 + */ + const readSeq = (count: number): number[] | null => { + if (count === 0) return []; + if (isGidFormat) { + const out: number[] = []; + for (let k = 0; k < count; k++) { + const m = remapGid(origToNew, r.u16(p + k * 2)); + if (m === null) return null; + out.push(m); + } + return out; + } + const out2: number[] = []; + for (let k = 0; k < count; k++) out2.push(r.u16(p + k * 2)); + return out2; + }; + const back = readSeq(backCount); + if (back === null) return null; p += backCount * 2; const inputCount = r.u16(p); /** inputCount 为 0 是异常(ChainSubRule 至少含第一分量,inputCount>=1),返回 null 跳过该规则 */ if (inputCount === 0 || inputCount > 255) return null; p += 2; /** input 序列不含第一分量(第一分量由 coverage/class 决定) */ - const inputRaw: number[] = []; - for (let k = 0; k < inputCount - 1; k++) inputRaw.push(r.u16(p + k * 2)); + const input = readSeq(inputCount - 1); + if (input === null) return null; p += (inputCount - 1) * 2; const lookCount = r.u16(p); if (lookCount > 255) return null; p += 2; - const lookRaw: number[] = []; - for (let k = 0; k < lookCount; k++) lookRaw.push(r.u16(p + k * 2)); + const look = readSeq(lookCount); + if (look === null) return null; p += lookCount * 2; const seqCount = r.u16(p); if (seqCount > 255) return null; @@ -861,23 +881,6 @@ function parseChainRuleFormat1or2( for (let k = 0; k < seqCount; k++) { records.push({ seq: r.u16(p + k * 4), lookup: r.u16(p + k * 4 + 2) }); } - - /** format1:gid 重映射,子集外 gid 则规则失效;format2:class index 原样保留 */ - const remapSeq = (arr: number[]): number[] | null => { - if (!isGidFormat) return arr.slice(); - const out: number[] = []; - for (const v of arr) { - const m = remapGid(origToNew, v); - if (m === null) return null; - out.push(m); - } - return out; - }; - - const back = remapSeq(backRaw); - const input = remapSeq(inputRaw); - const look = remapSeq(lookRaw); - if (back === null || input === null || look === null) return null; return { back, input, look, records }; }