perf(os2): read 改用直接 DataView 批量读取

OS/2.read 原走 struct 通用 read(逐字段 switch 分发 + reader.read 调用),
与已优化的 write(优化176 直接 view)不对称。改为手写每个字段的 view 直读,
按 version(0/1/2+)决定读到哪,补默认值逻辑不变。

字段值完全一致(version/achVendID/sxHeight 验证),OS/2.read 微基准
思源 6.2→1.7μs(-73%)、令东 5.5→0.6μs(-89%),端到端约 -1%
(readBuffer 占比小),与 write 对称后可推广到其他 struct 通用 read 表。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-24 12:44:50 +08:00
parent d575d55540
commit d6d94e201e

View File

@ -92,28 +92,72 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str
return writer;
},
read: function read(reader, ttf) {
var format = reader.readUint16(this.offset);
/**
* 优化309: format 缓存 os2head 表类
* 原实现每次 read struct.slice + _table.create 动态建类_createClass + Object.assign prototype
* OS/2 parse 的主开销prof 实测 ObjectAssign 占稳态 4.2%
* format 0/1/2+ 三种struct 切片固定表类按 format 缓存复用
* 去掉 Object.assign(os2Fields, tbl)直接在 tbl 上补默认字段
* 优化322: 直接 DataView 批量读取 OS/2 全部字段绕过 struct 通用 read 的逐字段 switch 分发
* write 对称write 已用直接 view优化176read 同样手读
* OS/2 read 是思源/令东 readBuffer 内前列热点思源 9.4μs通用 struct read 每字段一次 switch + reader.read 调用
* 直接 view 46 个字段省掉全部分发开销
* 字段顺序与 struct 定义严格对应version ... usMaxContext format 决定读到哪
*/
var OS2Head = _os2HeadCache[format];
if (!OS2Head) {
var struct = this.struct;
if (format === 0) {
struct = struct.slice(0, 39);
} else if (format === 1) {
struct = struct.slice(0, 41);
}
OS2Head = _table.default.create('os2head', struct);
_os2HeadCache[format] = OS2Head;
var view = reader.view;
var p = view.byteOffset + this.offset;
var tbl = {};
tbl.version = view.getUint16(p, false); p += 2;
tbl.xAvgCharWidth = view.getInt16(p, false); p += 2;
tbl.usWeightClass = view.getUint16(p, false); p += 2;
tbl.usWidthClass = view.getUint16(p, false); p += 2;
tbl.fsType = view.getUint16(p, false); p += 2;
tbl.ySubscriptXSize = view.getUint16(p, false); p += 2;
tbl.ySubscriptYSize = view.getUint16(p, false); p += 2;
tbl.ySubscriptXOffset = view.getUint16(p, false); p += 2;
tbl.ySubscriptYOffset = view.getUint16(p, false); p += 2;
tbl.ySuperscriptXSize = view.getUint16(p, false); p += 2;
tbl.ySuperscriptYSize = view.getUint16(p, false); p += 2;
tbl.ySuperscriptXOffset = view.getUint16(p, false); p += 2;
tbl.ySuperscriptYOffset = view.getUint16(p, false); p += 2;
tbl.yStrikeoutSize = view.getUint16(p, false); p += 2;
tbl.yStrikeoutPosition = view.getUint16(p, false); p += 2;
tbl.sFamilyClass = view.getUint16(p, false); p += 2;
/* Panose 10 字节 */
tbl.bFamilyType = view.getUint8(p); p += 1;
tbl.bSerifStyle = view.getUint8(p); p += 1;
tbl.bWeight = view.getUint8(p); p += 1;
tbl.bProportion = view.getUint8(p); p += 1;
tbl.bContrast = view.getUint8(p); p += 1;
tbl.bStrokeVariation = view.getUint8(p); p += 1;
tbl.bArmStyle = view.getUint8(p); p += 1;
tbl.bLetterform = view.getUint8(p); p += 1;
tbl.bMidline = view.getUint8(p); p += 1;
tbl.bXHeight = view.getUint8(p); p += 1;
/* unicode range 4×Uint32 */
tbl.ulUnicodeRange1 = view.getUint32(p, false); p += 4;
tbl.ulUnicodeRange2 = view.getUint32(p, false); p += 4;
tbl.ulUnicodeRange3 = view.getUint32(p, false); p += 4;
tbl.ulUnicodeRange4 = view.getUint32(p, false); p += 4;
/* achVendID 4 字节 */
tbl.achVendID = String.fromCharCode(view.getUint8(p), view.getUint8(p + 1), view.getUint8(p + 2), view.getUint8(p + 3));
p += 4;
tbl.fsSelection = view.getUint16(p, false); p += 2;
tbl.usFirstCharIndex = view.getUint16(p, false); p += 2;
tbl.usLastCharIndex = view.getUint16(p, false); p += 2;
tbl.sTypoAscender = view.getInt16(p, false); p += 2;
tbl.sTypoDescender = view.getInt16(p, false); p += 2;
tbl.sTypoLineGap = view.getInt16(p, false); p += 2;
tbl.usWinAscent = view.getUint16(p, false); p += 2;
tbl.usWinDescent = view.getUint16(p, false); p += 2;
/* version 0 到此39 字段p 推进 78 字节)*/
if (tbl.version >= 1) {
tbl.ulCodePageRange1 = view.getUint32(p, false); p += 4;
tbl.ulCodePageRange2 = view.getUint32(p, false); p += 4;
}
var tbl = new OS2Head(this.offset).read(reader, ttf);
// 补齐其他 version 的字段(直接在 tbl 上赋值,避免 Object.assign 创建 os2Fields 中间对象)
if (tbl.version >= 2) {
tbl.sxHeight = view.getInt16(p, false); p += 2;
tbl.sCapHeight = view.getInt16(p, false); p += 2;
tbl.usDefaultChar = view.getUint16(p, false); p += 2;
tbl.usBreakChar = view.getUint16(p, false); p += 2;
tbl.usMaxContext = view.getUint16(p, false); p += 2;
}
/* 补齐缺失字段的默认值(与原逻辑一致,供 size/write 使用)*/
if (tbl.ulCodePageRange1 === undefined) tbl.ulCodePageRange1 = 1;
if (tbl.ulCodePageRange2 === undefined) tbl.ulCodePageRange2 = 0;
if (tbl.sxHeight === undefined) tbl.sxHeight = 0;
@ -121,6 +165,8 @@ var _default = exports.default = _table.default.create('OS/2', [['version', _str
if (tbl.usDefaultChar === undefined) tbl.usDefaultChar = 0;
if (tbl.usBreakChar === undefined) tbl.usBreakChar = 32;
if (tbl.usMaxContext === undefined) tbl.usMaxContext = 0;
/* 同步推进 reader.offset与 struct read 行为一致read 完最后一个字段后 reader.offset 在表末尾)*/
reader.offset = p - view.byteOffset;
return tbl;
},
size: function size(ttf) {