Files
pdfcraft/crates
Grzegorz MartensandClaude Opus 5.5 2976caeb32 M8: the forms redaction follows through resources are read one operator at a time
resource_ops parsed the content and each form it follows whole before
checking its 2,000,000-operator limit, and content_draws parsed whole
streams with no limit at all. A parsed operator takes about 120 bytes,
and a form of `n` operators outside the mark cost 78 to 138 bytes of
memory per byte of content. Measured as the growth of the process's
peak: 629 MiB at 8 MiB, 1,253 MiB at 16 MiB and 4,415 MiB at 32 MiB, so
tens of GiB at the 256 MiB the page's allowance admits, from a file of
a few hundred KiB. The page's check of what it still
draws (prune), added in this branch, reads such forms on pages with no
fonts or properties too. On main the font and property cleanups read
them, and the unlinking check (content_draws) on any page that shares a
dictionary with a removed image, so the content_draws half fixes what
main does too.

Both now read with pdfcraft_content::ops, one operator at a time.
resource_ops holds at most one past its limit, and content_draws holds
no operators, as it only looks for Do. Measured as the peak resident memory of
the whole process (dev profile): the page's check over a form of 8, 16
and 64 MiB of `n` takes 311, 327 and 422 MiB, and the unlinking check
over one of 16 MiB takes 69 MiB, against 1,269 before (2,527 MiB at
32 MiB).

This bounds the operators held, not their operands: the parser still
holds the operands that no operator ends, so content of operands alone
costs about 30 bytes per byte (974 MiB for 32 MiB of `0`). Bounding
those is pdfcraft-content's, for every reader of content.

It also drops resource_ops' check that it followed no more than
2,000,000 forms. Each form it follows is a Do among the operators it
holds, which the operator limit already bounds, so that check could
never fail; cargo-mutants found it untested.

Test the_forms_followed_through_resources_are_read_one_operator_at_a_time
(tests/operator_memory.rs, a binary of its own) measures how much the
process's peak grows over 32 MiB of `n` operators through each check, on
Linux, and fails with either read reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-06 17:24:56 +01:00
..
2026-11-03 09:20:23 -05:00
…