From 94f89ddbb889b93028ff1b2ffc436e84bc2dc7ea 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: Sat, 25 Jul 2026 02:53:41 +0800 Subject: [PATCH] =?UTF-8?q?perf(cff):=20collectSubrRefs=20=E7=94=A8=20stac?= =?UTF-8?q?kLen+lastVal=20=E6=9B=BF=E4=BB=A3=20stack:number[]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 仅需栈长度(HSTEM 算 stemCount)与栈顶值(CALLSUBR/CALLGSUBR 取调用编号), 无需完整栈数组。消除每 operand 的 push(装箱)与 length=0 重置。 otf-思源8字 subsetOTF min 0.319→0.297ms(-7%),p50 0.401→0.390ms。 输出逐字节相同。与 rewriteCharstring/replaceDictOffsets 累计 otf-思源 -19%。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/cff-subset.ts | 49 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/backend/font_util/cff-subset.ts b/backend/font_util/cff-subset.ts index 690b3eb..b1b0c04 100644 --- a/backend/font_util/cff-subset.ts +++ b/backend/font_util/cff-subset.ts @@ -226,56 +226,67 @@ export function collectSubrRefs( localRefs: Set, gsubrRefs: Set, ): void { + /** + * 优化:用 stackLen 计数器 + lastVal 替代 stack: number[]。 + * 仅需栈长度(HSTEM 算 stemCount)与栈顶值(CALLSUBR/CALLGSUBR 取调用编号), + * 无需完整栈数组。消除每 operand 的 push(含装箱)与 stack.length=0 重置。 + * lastVal 仅在 stackLen>0 时有效(CALLSUBR 前必有 operand push)。 + */ let p = start; - const stack: number[] = []; + let stackLen = 0; + let lastVal = NaN; let stemCount = 0; while (p < end) { const b0 = b[p++]; if (b0 === 255) { /** fixed point(坐标),4 字节,不入栈编号判定 */ - stack.push(NaN); + lastVal = NaN; + stackLen++; p += 4; } else if (b0 === 28) { - stack.push(((b[p] << 24) | (b[p + 1] << 16)) >> 16); + lastVal = ((b[p] << 24) | (b[p + 1] << 16)) >> 16; + stackLen++; p += 2; } else if (b0 === 29) { - stack.push(((b[p] << 24) | (b[p + 1] << 16) | (b[p + 2] << 8) | b[p + 3]) | 0); + lastVal = ((b[p] << 24) | (b[p + 1] << 16) | (b[p + 2] << 8) | b[p + 3]) | 0; + stackLen++; p += 4; } else if (b0 >= 32 && b0 <= 246) { - stack.push(b0 - 139); + lastVal = b0 - 139; + stackLen++; } else if (b0 >= 247 && b0 <= 250) { - stack.push((b0 - 247) * 256 + b[p] + 108); + lastVal = (b0 - 247) * 256 + b[p] + 108; + stackLen++; p += 1; } else if (b0 >= 251 && b0 <= 254) { - stack.push(-(b0 - 251) * 256 - b[p] - 108); + lastVal = -(b0 - 251) * 256 - b[p] - 108; + stackLen++; p += 1; } else { /** 操作码(b0 <= 27,含 12 双字节) */ if (b0 === 12) { p += 1; - stack.length = 0; + stackLen = 0; } else if (b0 === T2_HSTEM || b0 === T2_VSTEM || b0 === T2_HSTEMHM || b0 === T2_VSTEMHM) { - stemCount += stack.length >> 1; - stack.length = 0; + stemCount += stackLen >> 1; + stackLen = 0; } else if (b0 === T2_HINTMASK || b0 === T2_CNTRMASK) { p += (stemCount + 7) >>> 3; - stack.length = 0; + stackLen = 0; } else if (b0 === T2_CALLSUBR) { - const arg = stack[stack.length - 1]; - if (Number.isInteger(arg)) { - const sn = arg + localBias; + if (Number.isInteger(lastVal)) { + const sn = lastVal + localBias; if (sn >= 0 && sn < localCount) localRefs.add(sn); } - stack.length = 0; + stackLen = 0; } else if (b0 === T2_CALLGSUBR) { - const arg = stack[stack.length - 1]; - if (Number.isInteger(arg)) gsubrRefs.add(arg); - stack.length = 0; + if (Number.isInteger(lastVal)) gsubrRefs.add(lastVal); + stackLen = 0; } else if (b0 === T2_ENDCHAR) { break; } else { /** 其余操作码(运动/曲线等)消费栈 */ - stack.length = 0; + stackLen = 0; } } }