Skip to content

Bound the shaping caches' work and memory on hostile fonts #297

Closed

Description

@albedosehen

Found by the independent review of #295 (merged as 8b04190). Line numbers are for main 8b04190.

Blocker: the subtable-cache rank is quadratic (src/shape/lazy.rs:106-131). get_with_digests calls has_cache(index) each time a cursor reaches a class-based (format 2) context subtable at index 8 or above, and has_cache rescans 0..index, so the cost per glyph is O(N²) in the lookup's subtable count; 0.23.1 was O(N). Open Sans with a 64 KB GSUB (one ccmp lookup with 32,000 offsets to one ChainContextFormat2 covering "a" with no class sets), shaping "aaaa": 0.23.1 5.9 ms, HarfBuzz 0.6 ms, main 7,141 ms. Output is identical.

Digest storage is not bounded by font size (src/tables/layout/accel.rs:153-177, :333, :344). LookupAccel::build stores 24 bytes per subtable for every lookup index it applies, even after both reading budgets are spent. A 66 KB GSUB (500 lookup indices sharing one Lookup of 32,000 format 3 subtables), shaping "a" twice: 381 MB peak against 12 MB on 0.23.1; 65,535 indices (about 330 KB) would need about 50 GB. Charge stored digests to the table budget and store an empty (admit-all) digest past it.

Vertical long runs: Hahmlet[wght] at wght 700, TTB, 100 characters on a warm Font: 0.23.0 64.8 µs, main 88.7 µs. The 8-character run is faster than 0.23.0 (11.28 to 6.71 µs). Some per-glyph vertical work still runs on every call for long runs; find it, or the 0.24.0 changelog must not claim varied glyf vertical runs are faster than 0.23.0.

Smaller items:

  • src/ot/layout_select.rs:570: past 16 (script, language, FeatureVariations record) keys, every feature query builds a whole map instead of walking the language system; slots never free. Return None past the cap to fall back to the walk.
  • docs/STABILITY.md: say that Font::coords is no longer const.
  • Document that the core crate needs atomics with compare-and-swap (thumbv6m-none-eabi does not build; it already failed before Cut per-call shaping cost with per-Font accelerators, plans and metric caches #295 through alloc::sync).
  • The shape fuzz target reuses a warm Font; assert that its output equals a fresh Font's.

Activity

  1. added this to the 0.24.0 milestone on Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions