From 98b56b4f35adb7c41bf8ae0926a9efd11f79d19a 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 23:12:05 +0800 Subject: [PATCH] =?UTF-8?q?perf(otf):=20assembleSfnt=20wholeSum=20?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=E5=90=84=E8=A1=A8=20checksum=EF=BC=8C?= =?UTF-8?q?=E6=B6=88=E9=99=A4=E5=85=A8=E6=96=87=E4=BB=B6=E4=BA=8C=E6=AC=A1?= =?UTF-8?q?=E9=81=8D=E5=8E=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit head.checkSumAdjustment 原先两遍遍历 ~750KB 输出: 1. calcTableChecksum 对每表(CFF 742KB 最大)算 checksum 2. wholeSum 再次遍历整个 out 文件 4 字节步进算全文件 sum 数学等价:全文件 sum = 目录区 sum + Σ 各表数据段 checksum, 而各表数据段 padded sum 正是写表记录时已算的 calcTableChecksum。 故 wholeSum 改为「dirSize(156B) 目录区 sum + 累加的 tablesDataSum」, 消除数百 KB 大表的第二次整段遍历。 思源 otf 8字 subsetOTF:min 1.836→1.321ms(-28%)、p50 2.433→1.720ms(-29%); 字节级零回归(cmp 优化前后输出逐字节一致,含 checkSumAdjustment); 基准测试全字体 SSIM 与优化前完全一致。 Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/font_util/otf-subset.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/backend/font_util/otf-subset.ts b/backend/font_util/otf-subset.ts index f78edf0..77ac9fb 100644 --- a/backend/font_util/otf-subset.ts +++ b/backend/font_util/otf-subset.ts @@ -404,9 +404,11 @@ function assembleSfnt(tables: { tag: string; bytes: Uint8Array }[]): Uint8Array dv.setUint16(8, entrySelector, false); dv.setUint16(10, rangeShift, false); - /** 表记录 + 数据 */ + /** 表记录 + 数据;各表 checksum 同时累加进 wholeSum(数据段 sum 等于表 checksum) */ let dataOff = dirSize; const headIdx = tables.findIndex((t) => t.tag === "head"); + /** wholeSum = Σ 各表数据段 checksum。head.checkSumAdjustment 此刻为 0(passthroughHead 已清零),目录区稍后补算 */ + let tablesDataSum = 0; for (let i = 0; i < numTables; i++) { const r = 12 + i * 16; out[r] = tables[i].tag.charCodeAt(0); @@ -414,6 +416,7 @@ function assembleSfnt(tables: { tag: string; bytes: Uint8Array }[]): Uint8Array out[r + 2] = tables[i].tag.charCodeAt(2); out[r + 3] = tables[i].tag.charCodeAt(3); const checksum = calcTableChecksum(tables[i].bytes); + tablesDataSum = (tablesDataSum + checksum) >>> 0; dv.setUint32(r + 4, checksum, false); dv.setUint32(r + 8, dataOff, false); dv.setUint32(r + 12, tables[i].bytes.length, false); @@ -421,20 +424,18 @@ function assembleSfnt(tables: { tag: string; bytes: Uint8Array }[]): Uint8Array dataOff += paddedLens[i]; } - /** head.checkSumAdjustment = 0xB1B0AFBA - 全文件 sum */ + /** head.checkSumAdjustment = 0xB1B0AFBA - 全文件 sum。 + * 全文件 sum = 目录区 sum + Σ 各表数据段 checksum。 + * 数据段 sum 已在写表时累加(=各表 checksum),目录区仅 dirSize 字节(sfnt 头+表记录),单独遍历即可, + * 避免对数百 KB 的 CFF 等大表做第二次整段遍历。 */ if (headIdx >= 0) { const headRecOff = 12 + headIdx * 16; const headDataOff = dv.getUint32(headRecOff + 8, false); - /** 先算全文件 checksum(head.checkSumAdjustment 此时为 0) */ - let wholeSum = 0; - const wholePadded = (out.length + 3) & ~3; - for (let i = 0; i < wholePadded; i += 4) { - const b0 = out[i] || 0; - const b1 = out[i + 1] || 0; - const b2 = out[i + 2] || 0; - const b3 = out[i + 3] || 0; - wholeSum = (wholeSum + ((b0 << 24) | (b1 << 16) | (b2 << 8) | b3)) >>> 0; + let dirSum = 0; + for (let i = 0; i < dirSize; i += 4) { + dirSum = (dirSum + ((out[i] << 24) | (out[i + 1] << 16) | (out[i + 2] << 8) | out[i + 3])) >>> 0; } + const wholeSum = (dirSum + tablesDataSum) >>> 0; const adjustment = (0xb1b0afba - wholeSum) >>> 0; dv.setUint32(headDataOff + 8, adjustment, false); }