From 089ae7c0a769e13bf48c08a8daf0c0765384e32f 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 12:53:04 +0800 Subject: [PATCH] =?UTF-8?q?perf(name):=20read=20=E5=8D=95=E9=81=8D?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=AE=9A=20platform=EF=BC=8C=E6=B6=88?= =?UTF-8?q?=E9=99=A4=E5=85=A8=E9=83=A8=E4=B8=AD=E9=97=B4=20nameRecord=20?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原 read 先建 count 个 {platform,encoding,language,nameId,length,offset} 中间对象, 再两遍遍历它们。改为两趟直扫 view: pass1 只读 platform/encoding/language 三字段比较定 platform(windows english 优先), 不读 nameId/length/offset、不建对象; pass2 头开始只对匹配记录读 nameId/length/offset + readBytes + 解码,直接产出 names。 思源 56 条记录中间对象 56→0,view 读次数减半。字节 sha256 完全一致, 5 轮交替 readBuffer median 稳定 -3~-5%(对象分配本身 V8 近零成本,收益小但一致)。 Co-Authored-By: Claude Opus 4.8 (1M context) --- vendor/fonteditor-core/lib/ttf/table/name.js | 66 ++++++++++---------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/vendor/fonteditor-core/lib/ttf/table/name.js b/vendor/fonteditor-core/lib/ttf/table/name.js index 15c5e1c..83251be 100644 --- a/vendor/fonteditor-core/lib/ttf/table/name.js +++ b/vendor/fonteditor-core/lib/ttf/table/name.js @@ -25,51 +25,53 @@ var _default = exports.default = _table.default.create('name', [], { nameTbl.count = view.getUint16(vOffset, false); vOffset += 2; nameTbl.stringOffset = view.getUint16(vOffset, false); vOffset += 2; var count = nameTbl.count; - var nameRecordTbl = new Array(count); - for (var i = 0; i < count; ++i) { - nameRecordTbl[i] = { - platform: view.getUint16(vOffset, false), - encoding: view.getUint16(vOffset + 2, false), - language: view.getUint16(vOffset + 4, false), - nameId: view.getUint16(vOffset + 6, false), - length: view.getUint16(vOffset + 8, false), - offset: view.getUint16(vOffset + 10, false) - }; - vOffset += 12; - } - reader.offset = vOffset - view.byteOffset; - - var baseOffset = offset + nameTbl.stringOffset; - var names = {}; + /** + * 优化324: 单遍扫描确定 platform + 直接产出 names,消除全部中间 nameRecord 对象。 + * 原实现先建 count 个 {platform,encoding,language,nameId,length,offset} 对象(思源 56 个), + * 再两遍遍历。改为: + * pass1 只读 platform/encoding/language 三字段比较,确定最终 platform(windows english 优先), + * 不读 nameId/length/offset、不建对象; + * pass2 头开始只对匹配 platform+encoding+language 的记录读 nameId/length/offset + readBytes + 解码。 + * 思源 56 条记录只约 15 条匹配,对象分配 56→0、第二遍 view 读次数减半。 + */ + var dirStart = vOffset; var platform = _platform.default.Macintosh; var encoding = _encoding.mac.Default; var language = 0; - - /* 检查是否有 windows english name */ - /** 优化291: 缓存 nameRecordTbl[k] 避免重复属性查找 */ - for (var k = 0; k < count; k++) { - var nr = nameRecordTbl[k]; - if (nr.platform === _platform.default.Microsoft && nr.encoding === _encoding.win.UCS2 && nr.language === 1033) { + var p = dirStart; + for (var i = 0; i < count; ++i) { + if (view.getUint16(p, false) === _platform.default.Microsoft + && view.getUint16(p + 2, false) === _encoding.win.UCS2 + && view.getUint16(p + 4, false) === 1033) { platform = _platform.default.Microsoft; encoding = _encoding.win.UCS2; language = 1033; break; } + p += 12; } - /** - * 优化323: 延迟 readBytes——原实现对全部 count 条记录都 readBytes(slice 一份字节), - * 但最终只有匹配 platform+encoding+language 且 nameId 有用的记录才解码字符串, - * 其余记录的 .name 字节从不被消费。思源 56 条记录只解码约 15 条,浪费 41 次 slice。 - * 这里在第三循环里按需对匹配记录 readBytes,把 N 次 slice 降到「有用记录数」次。 - */ + reader.offset = dirStart + count * 12 - view.byteOffset; + + var baseOffset = offset + nameTbl.stringOffset; + var names = {}; + /** 优化323: 按需 readBytes,只对匹配记录解码(N 次 slice → 有用记录数次) */ var isUTF8 = language === 0; + var p2 = dirStart; for (var m = 0; m < count; ++m) { - var nameRecord = nameRecordTbl[m]; - if (nameRecord.platform === platform && nameRecord.encoding === encoding && nameRecord.language === language && _nameId.default[nameRecord.nameId]) { - var nameBytes = reader.readBytes(baseOffset + nameRecord.offset, nameRecord.length); - names[_nameId.default[nameRecord.nameId]] = isUTF8 ? _string.default.getUTF8String(nameBytes) : _string.default.getUCS2String(nameBytes); + if (view.getUint16(p2, false) === platform + && view.getUint16(p2 + 2, false) === encoding + && view.getUint16(p2 + 4, false) === language) { + var nameId = view.getUint16(p2 + 6, false); + var nameKeyId = _nameId.default[nameId]; + if (nameKeyId) { + var len = view.getUint16(p2 + 8, false); + var recOff = view.getUint16(p2 + 10, false); + var nameBytes = reader.readBytes(baseOffset + recOff, len); + names[nameKeyId] = isUTF8 ? _string.default.getUTF8String(nameBytes) : _string.default.getUCS2String(nameBytes); + } } + p2 += 12; } return names; },