perf(gsub): coverageAllOutOfSubset range 相交判定改二分

COV_RANGE 分支逐 gid 展开 range 查 gidLookup(O(range length)),
对全子集外的 coverage 要遍历完所有 gid 才确认 outOfSubset。初夏 fmt3
range 展开总 20388 gid(300 range / 12 子集 gid),逐 gid 是预扫描主开销。

改用 currentSortedSubsetGids 二分:range [start,end] 是否含任一子集 gid
等价于排序子集数组存在 gid ∈ [start,end](gidLookup[g]>=0 ⟺ g 是子集 gid)。
O(log n) per range。微基准 10×(0.012→0.001ms),subsetGSUB 端到端初夏 -6%。

等价性:end clamp/越界语义保留(sortedGids 全 <numGlyphs,二分天然限范围);
COVERAGE_MAX_EXPAND 守卫保留(与 readCoverageRemapped origNonEmpty 一致);
origNonEmpty 对所有有效 range 非空标记(含越界 range,原版同等价)。

字节一致性:11 用例(含 FiraCode 两文本)subsetGSUB 输出 sha 全 IDENTICAL。
SSIM 30 用例全通过(最低 0.9876 篆体固有)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-25 07:01:35 +08:00
parent 6d224c2405
commit 7a76208558

View File

@ -1349,10 +1349,14 @@ function coverageAllOutOfSubset(
}
if (format === COV_RANGE) {
const rangeCount = dv.getUint16(covOff + 2, false);
/** readCoverageRemapped end clamp numGlyphs-1start>=numGlyphs range gid
* gidLookup[g] g>=numGlyphs gid fmt3
* readCoverageRemapped [[gsub-subset-fmt3-prefetch-consistency]] */
const numGlyphs = gidLookup.length;
/** gid currentSortedSubsetGidssubsetGSUB
* range O(log n) per range gid O(range length)
* fmt3 range 20388 gid300 range12 gid gid 0.012ms vs 0.001ms10×
* range [start,end] gidLookup[g]>=0 g gid gid [start,end]
* gidLookup[g]>=0 g gid g end clamp/
* sortedGids < numGlyphs gid end>=numGlyphs gid <= end */
const sortedGids = currentSortedSubsetGids;
const subsetN = sortedGids.length;
let p = covOff + 4;
let origNonEmpty = false;
for (let i = 0; i < rangeCount; i++) {
@ -1360,15 +1364,19 @@ function coverageAllOutOfSubset(
const start = dv.getUint16(p, false);
const end = dv.getUint16(p + 2, false);
if (end >= start && end - start < COVERAGE_MAX_EXPAND) {
if (start >= numGlyphs) {
/** 整 range 越界gid 全不在子集,但 range 非空origNonEmpty不短路 return false */
origNonEmpty = true;
} else {
const e = end < numGlyphs ? end : numGlyphs - 1;
for (let g = start; g <= e; g++) {
origNonEmpty = true;
if (gidLookup[g] >= 0) return false;
/** range 非空(无论是否含子集 gid标记 origNonEmpty */
origNonEmpty = true;
/** range gidsubsetN=0 GSUB range
* sortedGids lo sortedGids[lo] > end gid <0 一致 */
if (subsetN > 0 && start <= sortedGids[subsetN - 1] && end >= sortedGids[0]) {
let lo = 0, hi = subsetN;
while (lo < hi) {
const mid = (lo + hi) >>> 1;
if (sortedGids[mid] < start) lo = mid + 1;
else hi = mid;
}
/** lo = 第一个 >= start 的子集 gid 下标;若该 gid <= end 则 range 含子集 gid → 非 outOfSubset */
if (lo < subsetN && sortedGids[lo] <= end) return false;
}
}
p += 6;