perf(ot-bytes): OTWriter 初始容量 256→2048 + 内联容量检查消除热路径函数调用

subsetGPOS 思源 36→33μs、subsetGSUB 13→11μs;writeUint16 是序列化第一热点
(思源 207次/call),原 per-call ensure() 函数调用在 V8 class private method
不内联;内联为容量判断 + grow,并预分配 2048 避免小输出多次扩容拷贝
This commit is contained in:
崮生(子虚) 2026-07-24 14:40:05 +08:00
parent 04bf5c6077
commit 852f0d2f21

View File

@ -25,8 +25,15 @@ export class OTWriter {
/** Uint8Array number[] + push writeUint8/16
* gsub-subset writeUint8 ScriptList/FeatureList
* Uint8Array + size writeUint8/16
* writeBytes TypedArray.set toUint8Array subarray */
private buf: Uint8Array = new Uint8Array(256);
* writeBytes TypedArray.set toUint8Array subarray
*
* 优化327: 初始容量 2562048 writeUint16/writeInt16/reserveOffset16
* GPOS/GSUB KB GPOS 978B GSUB KB256 2×
* new Uint8Array + set writeUint16
* subsetGPOS 207 /call ~58% private ensure()V8 class
* private method per-call
* grow */
private buf: Uint8Array = new Uint8Array(2048);
private size: number = 0;
private patches: Array<{ pos: number; base: number; targetGetter: () => number }> = [];
@ -34,10 +41,8 @@ export class OTWriter {
return this.size;
}
/** 确保剩余容量 >= need不足则按 2× 扩容 */
private ensure(need: number): void {
const required = this.size + need;
if (required <= this.buf.byteLength) return;
/** 容量不足时扩容(仅在 write 路径内联判断发现不够时调用) */
private grow(required: number): void {
let cap = this.buf.byteLength;
while (cap < required) cap *= 2;
const grown = new Uint8Array(cap);
@ -55,13 +60,15 @@ export class OTWriter {
}
writeUint8(v: number): void {
this.ensure(1);
this.buf[this.size++] = v & 0xff;
const s = this.size;
if (s + 1 > this.buf.byteLength) this.grow(s + 1);
this.buf[s] = v & 0xff;
this.size = s + 1;
}
writeUint16(v: number): void {
this.ensure(2);
const s = this.size;
if (s + 2 > this.buf.byteLength) this.grow(s + 2);
this.buf[s] = (v >>> 8) & 0xff;
this.buf[s + 1] = v & 0xff;
this.size = s + 2;
@ -70,16 +77,18 @@ export class OTWriter {
/** 批量写入字节块TypedArray.set远快于逐字节 writeUint8 循环) */
writeBytes(arr: Uint8Array): void {
const n = arr.byteLength;
this.ensure(n);
this.buf.set(arr, this.size);
this.size += n;
const s = this.size;
const required = s + n;
if (required > this.buf.byteLength) this.grow(required);
this.buf.set(arr, s);
this.size = required;
}
/** int16 SingleSubst format1 deltaGlyphID
* number[] length Uint8Array ensure + size */
writeInt16(v: number): void {
this.ensure(2);
const s = this.size;
if (s + 2 > this.buf.byteLength) this.grow(s + 2);
const u16 = v < 0 ? 0x10000 + (v & 0xffff) : v & 0xffff;
this.buf[s] = (u16 >>> 8) & 0xff;
this.buf[s + 1] = u16 & 0xff;
@ -96,9 +105,9 @@ export class OTWriter {
/** 预留一个 uint16 偏移量槽位flush 时写入 (targetGetter() - base) */
reserveOffset16(base: number, targetGetter: () => number): void {
this.ensure(2);
const pos = this.size;
this.size += 2;
if (pos + 2 > this.buf.byteLength) this.grow(pos + 2);
this.size = pos + 2;
this.patches.push({ pos, base, targetGetter });
}