Skip to content

refactor: replace single-retry unresolved loop with fixed-point itera…#127

Merged
Velli20 merged 1 commit intomainfrom
forward-resolve-objects
Apr 12, 2026
Merged

refactor: replace single-retry unresolved loop with fixed-point itera…#127
Velli20 merged 1 commit intomainfrom
forward-resolve-objects

Conversation

@Velli20
Copy link
Copy Markdown
Owner

@Velli20 Velli20 commented Apr 12, 2026

…tion

Replace the single-pass retry in load_objects_with_decryption with a convergent fixed-point loop that iteratively resolves forward references (e.g. indirect /Length entries in stream dictionaries).

Changes:

  • Extract try_load_object helper to deduplicate parse/decrypt/insert logic
  • Iterate retries until all resolved, no progress, or 16-iteration cap
  • Add UnresolvedObjects error variant with count, iterations, first_offset
  • Add tests for indirect /Length resolution and unresolvable references

…tion

Replace the single-pass retry in load_objects_with_decryption with a
convergent fixed-point loop that iteratively resolves forward references
(e.g. indirect /Length entries in stream dictionaries).

Changes:
- Extract try_load_object helper to deduplicate parse/decrypt/insert logic
- Iterate retries until all resolved, no progress, or 16-iteration cap
- Add UnresolvedObjects error variant with count, iterations, first_offset
- Add tests for indirect /Length resolution and unresolvable references

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors PdfReader object loading to resolve forward references (notably indirect /Length in streams) via a convergent fixed-point retry loop, improving robustness when xref iteration order encounters dependencies that haven’t been loaded yet.

Changes:

  • Adds PdfReaderError::UnresolvedObjects { count, iterations, first_offset } for cases where forward references can’t be resolved after iterative retries.
  • Replaces the prior single retry pass with an iterative retry loop capped at 16 iterations and stopping on no-progress convergence.
  • Introduces try_load_object helper and adds tests covering indirect /Length resolution and unresolvable references.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +690 to +698
assert!(result.is_err(), "Should fail for unresolvable reference");
let err_msg = match result {
Err(e) => e.to_string(),
Ok(_) => unreachable!(),
};
assert!(
err_msg.contains("failed to resolve"),
"Expected UnresolvedObjects error, got: {err_msg}"
);
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_unresolvable_reference_returns_error only asserts that the error string contains "failed to resolve", which would also pass for other error variants/messages. Consider matching on Err(PdfReaderError::UnresolvedObjects { .. }) (and optionally asserting count, iterations, and first_offset == obj4_offset) so the test reliably verifies the new error behavior.

Suggested change
assert!(result.is_err(), "Should fail for unresolvable reference");
let err_msg = match result {
Err(e) => e.to_string(),
Ok(_) => unreachable!(),
};
assert!(
err_msg.contains("failed to resolve"),
"Expected UnresolvedObjects error, got: {err_msg}"
);
match result {
Err(super::PdfReaderError::UnresolvedObjects {
count,
iterations,
first_offset,
..
}) => {
assert_eq!(count, 1, "Expected one unresolved object");
assert_eq!(iterations, 0, "Expected failure after one no-progress pass");
assert_eq!(
first_offset as usize,
obj4_offset,
"Expected unresolved object to be object 4"
);
}
Err(e) => panic!("Expected UnresolvedObjects error, got: {e}"),
Ok(_) => panic!("Should fail for unresolvable reference"),
}

Copilot uses AI. Check for mistakes.
@Velli20 Velli20 merged commit 82ab15a into main Apr 12, 2026
8 checks passed
@Velli20 Velli20 deleted the forward-resolve-objects branch April 12, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants