Repository navigation
A /Prev in a section a guard stops is judged otherwise than a whole read judges it #264
Description
The /Prev of a section a guard stops is judged otherwise than a whole read judges it: a reference to an object MaxObjectLength cuts is blamed on the file, and a fault the guarded read can see without reading past the guard goes unreported.
Batch 14 (#175) reads a /Prev or an /XRefStm written as a reference, in a section a guard ended the chain at, as a whole read reads it, unless it leads to what the guard hides. Two gaps are left, both older than the batch.
A reference to an object the object guard cuts, with no full index. ReadWithoutLoading reads the object /Prev names without recording it. When the object runs past MaxObjectLength, it reads as nothing. Since batch 14, the chain notes that the guard cut it, and a link so cut is unknown where a guard also ended the chain at that section, cutting its rows or filling the index. With MaxObjectLength alone, the section read whole, SectionOffset still reports the section missing: xref.section-missing, then the Error xref.section-not-found ("which no section read before it places where it can be read"), a rebuild, and file.root-invalid for the /Root the older trailer holds. A whole read follows /Prev with no fault. It is the /Prev counterpart of #263, which names /Filter and /Length.
Faults a section a guard stops does not report. SectionOffset is not called:
- when
MaxXRefSectionLengthcuts a classic table (theReadTrailerPastRowspath); - when a table fills
MaxIndexEntries; - when
MaxDecodedStreamLengthcuts a stream's rows.
When a cross-reference stream fills the index, the offset it gets is not followed. So the guarded read never reports a fault of that section's /Prev that it can see without reading past the guard:
- a reference to a free row;
- an offset outside the file;
- a
/Prevnaming the section itself.
A whole read reports xref.section-missing and the Error xref.section-not-found for the first two, and xref.chain-cycle and the Error xref.chain-loop for the third.
Shapes, reproduced by the eleventh review of batch 14 on its branch
- E1: cross-reference stream 22,
/Index [0 1 21 1 2 20],/Prev 21 0 R, no/Root. Object 21 is a comment line of 3,000 bytes, then the older table's offset. WithMaxObjectLength = 1024:xref.section-missing,limit.object,xref.rebuilt,file.root-invalid,xref.section-not-found. A whole read gives onlyfile.trailer-value-wrong. - F: the newest classic table's rows are
0 1 / 21 1 (free) / 2 2 / 4 40, and its trailer is<< /Size 44 /Prev 21 0 R >>, withMaxXRefSectionLength = 128. A whole read givesxref.section-missingandxref.section-not-found. The guarded read giveslimit.xref-section-length,xref.rebuiltandfile.root-invalid, but no finding on/Prev. - G: the same table, guarded the same way, with
/Prev 99999999(outside the file), or with/Previts own offset. The guarded read gives only the guard's reports; a whole read givesxref.section-not-found, orxref.chain-cycleandxref.chain-loop.
What closes it
- A link
/Prevleads through that the object guard cut is unknown, as one past a full index is: the chain ends there for the guard, which is reported, and nothing is blamed on the file. - A section a guard stops still judges its
/Prevas far as it can without reading past the guard: a reference to a free row, an offset outside the file, or the section itself is reported as a whole read reports it. - Tests in
ReaderLimitsTestsandCrossReferenceRuleTestsfor E1, F and G, each beside the same file read under the default limits.
Placed with batch 19, beside #188 and #197, the chain's reading of where its sections lie.
Found by the eleventh adversarial review of batch 14 (#129, #175).
Activity
docs: record batch 14 in status.md
AdCodicem commentedon Oct 23, 2026
Batch 16 (#263, branch
ccr-6d6c0ee6-dqm4lm-174) pays this issue's first gap. The second is left for batch 19.Paid: a reference to an object the object guard cuts (shape E1). A
/Prevor/XRefStmwritten as a reference to an objectMaxObjectLengthcuts now ends the chain for the guard, as/W,/Index,/Size,/Filter,/DecodeParmsand a deferred/Lengthdo. When the guard is reached, the reader:limit.objectonce the chain is read, or throws at opening underThrowOnLimit;xref.section-missing,xref.section-not-foundnorfile.root-invalidfollows;file.checked-in-partname the reference the guard cut.Batch 16 also gives the chain's own reads (
TryReadUnrecorded) the file's edge, so a/Prevobject ending exactly atMaxObjectLengthreads whole. A hybrid file's stream's own/Prev, which no reader follows, is not read at all:file.trailer-value-wrongjudges it as written.These tests hold it, in
GuardCutValueTests:A_section_offset_a_guard_cut_ends_the_chain_as_the_guard_s: a table's/Prevand a table's/XRefStm;A_cross_reference_stream_s_prev_a_guard_cut_ends_the_chain_as_the_guard_s: E1's shape;A_section_whose_prev_a_guard_cut_keeps_the_chain_it_hides_when_it_is_found_where_it_was_moved;A_chain_a_guard_ends_at_a_hidden_prev_leaves_unjudged_what_needs_the_whole_chain.Left for batch 19: a section a guard stops judges no fault of its
/Prev(shapes F and G).SectionOffsetis still not called when one of these guards stops a section:MaxXRefSectionLength;MaxDecodedStreamLength.So a reference to a free row, an offset outside the file, or a
/Prevnaming the section itself goes unreported under those guards. This issue stays open for that, beside #188 and #197.Generated by Claude Code
AdCodicem commentedon Oct 25, 2026
One variant of the paid shape remains, filed as #292. When the guard cuts the header of the object a
/Prevnames (9 0, a run of blanks, thenobj), rather than its value, the chain still ends for the guard. But once the chain is read, the load that reaches the guard follows the load rule for a header past the object guard, and rebuilds the index.On
a76c9daboth forms had the index rebuilt after a falsexref.section-missing. Batch 16 (#297) removes that report from both and the rebuild from the value cut.Generated by Claude Code