Tracking the low-severity findings from the libeot (MPL-2.0) parity review that were deliberately not changed in #5 / #6 — each is either cosmetic, unreachable on valid input, or an intentional JS-idiom divergence. Recorded so they aren't re-discovered as "bugs".
1. No EOT_OFF_BYTE_BOUNDARY enforcement (stream.ts)
libeot rejects a byte-level read/write/seek when the stream is mid-byte (bitPos != 0); it also refuses to reset bitPos on seek. Our Stream silently tolerates both. Unreached today: the only readNBits caller (ctf-parser glyph decoding) consumes whole bytes per point, so the stream is always byte-aligned at the boundary. Low value; would add guards to every byte accessor.
2. Bounding-box accumulator skips the int16 wrap (ctf-parser.ts)
libeot accumulates the int16-truncated coordinate delta into the running bbox min/max; we accumulate the raw value. Divergence is reachable only via the 16-bit triplet encodings (indices 124–127) with a raw delta > 32767 and a positive sign — which no sane encoder emits (it would use a smaller encoding). Affects only the computed bounding box, and only on malformed input. libeot's own code is itself sloppy here (implicit unsigned -> int16_t narrowing).
3. *ThroughReserve / copyTo grow instead of erroring (stream.ts)
libeot returns EOT_OUT_OF_RESERVED_SPACE / EOT_SEEK_PAST_EOS when a write/seek exceeds the reserved capacity; our Stream auto-grows (the natural JS idiom). Intentional — but it converts libeot's "reserved space exhausted" failure signal into a silent realloc. Kept as-is because auto-growth is the expected behavior for a JS builder.
4. sfnt-builder treats head as optional
dumpContainer patches head.checksumAdjustment inside if (headTable). Since parseCTF now requires head (v1.5.0), this branch is belt-and-suspenders — a head-less container can no longer reach it. Could be tightened to a hard error for defense in depth. Cosmetic.
5. Silent hdmx/VDMX skip
libeot logs a warning ("Ignoring hdmx/VDMX table — will be fixed in a future release") when it drops these tables; we drop them silently. The skip itself is correct. Purely a diagnostics gap.
Non-issues (confirmed, listed to close the loop)
- No adaptive-Huffman rescale: libeot has none either (
weight is a long; JS numbers are exact past any reachable input). Not a gap.
EOT_WARN_NOT_ENOUGH_GLYPHS / NOT_ENOUGH_SPACE_RESERVED: the flags that would raise these are initialized false and never assigned in libeot — dead code upstream, so their absence here is not a gap.
None of these affects a valid-input decode. Filing for traceability; happy to pick any up if they become load-bearing.
Tracking the low-severity findings from the libeot (MPL-2.0) parity review that were deliberately not changed in #5 / #6 — each is either cosmetic, unreachable on valid input, or an intentional JS-idiom divergence. Recorded so they aren't re-discovered as "bugs".
1. No
EOT_OFF_BYTE_BOUNDARYenforcement (stream.ts)libeot rejects a byte-level read/write/seek when the stream is mid-byte (
bitPos != 0); it also refuses to resetbitPoson seek. OurStreamsilently tolerates both. Unreached today: the onlyreadNBitscaller (ctf-parserglyph decoding) consumes whole bytes per point, so the stream is always byte-aligned at the boundary. Low value; would add guards to every byte accessor.2. Bounding-box accumulator skips the int16 wrap (
ctf-parser.ts)libeot accumulates the int16-truncated coordinate delta into the running bbox min/max; we accumulate the raw value. Divergence is reachable only via the 16-bit triplet encodings (indices 124–127) with a raw delta > 32767 and a positive sign — which no sane encoder emits (it would use a smaller encoding). Affects only the computed bounding box, and only on malformed input. libeot's own code is itself sloppy here (implicit
unsigned -> int16_tnarrowing).3.
*ThroughReserve/copyTogrow instead of erroring (stream.ts)libeot returns
EOT_OUT_OF_RESERVED_SPACE/EOT_SEEK_PAST_EOSwhen a write/seek exceeds the reserved capacity; ourStreamauto-grows (the natural JS idiom). Intentional — but it converts libeot's "reserved space exhausted" failure signal into a silent realloc. Kept as-is because auto-growth is the expected behavior for a JS builder.4.
sfnt-buildertreatsheadas optionaldumpContainerpatcheshead.checksumAdjustmentinsideif (headTable). SinceparseCTFnow requireshead(v1.5.0), this branch is belt-and-suspenders — ahead-less container can no longer reach it. Could be tightened to a hard error for defense in depth. Cosmetic.5. Silent hdmx/VDMX skip
libeot logs a warning ("Ignoring hdmx/VDMX table — will be fixed in a future release") when it drops these tables; we drop them silently. The skip itself is correct. Purely a diagnostics gap.
Non-issues (confirmed, listed to close the loop)
weightis along; JS numbers are exact past any reachable input). Not a gap.EOT_WARN_NOT_ENOUGH_GLYPHS/NOT_ENOUGH_SPACE_RESERVED: the flags that would raise these are initializedfalseand never assigned in libeot — dead code upstream, so their absence here is not a gap.None of these affects a valid-input decode. Filing for traceability; happy to pick any up if they become load-bearing.