Repository navigation
Fix DecodingReader::set_encoding panic on declarations longer than 64 bytes - #1041
emschwartz wants to merge 3 commits into
Conversation
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1041 +/- ##
==========================================
+ Coverage 53.71% 53.87% +0.15%
==========================================
Files 51 51
Lines 19412 19532 +120
==========================================
+ Hits 10428 10523 +95
- Misses 8984 9009 +25
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| // bytes decoded with a potentially wrong initial encoding. A longer | ||
| // declaration is read in several chunks of this size. | ||
| #[cfg(feature = "encoding")] | ||
| const PREFIX_CAP: usize = 64; |
There was a problem hiding this comment.
I also suggest to increase this to 128, so the standard prelude can be fully read in one chunk even in UTF-16 case.
<?xml version="1.0" encoding=""?>contains 33 characters + name of the encoding (the longest name of the supported encoding is "x-mac-cyrillic" which is 14 characters).
… bytes DecodingReader decodes its 64-byte prefix only up to the first `>`, so that set_encoding() takes effect right after the XML declaration. When the declaration did not fit in the prefix, the main decode path decoded the rest of the input as UTF-8: set_encoding() panicked, or reading failed earlier on non-ASCII content. Keep refilling the prefix until a `>` has been decoded, so it always ends after the declaration. A UTF-16 document whose declaration names an ASCII-compatible encoding such as `UTF-8` hit the same panic when the declaration did not fit in the prefix, and was decoded as garbage when it did. The declaration could only be read because the document is UTF-16, so set_encoding() now keeps the detected encoding in that case. Closes tafia#1040 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cover the Interrupted retry and error propagation in the prefix refill loop. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJX1e6MaZLgfVEdmH1eMn8
A standard declaration with the longest supported encoding name is 47 characters, which is 94 bytes in UTF-16 and did not fit in 64. Pad the tests' declarations so they still exceed the prefix. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJX1e6Wx7WdriMBUFMxHL3
3c690bb to
412976b
Compare
Fixes #1040.
Previously,
quick-xmlassumed that the XML declaration could be read out of only the first 64 bytes. However, there are valid documents that have declarations that are a few bytes longer than that.This PR changes the prefix reader so that it has a concept of whether the declaration is still open. It will keep reading the underlying stream looking for the declaration's closing
>, even if it goes beyond 64 bytes.This prevents a panic that would be caused by calling
set_encodingafter the 64 bytes and after the prefix bytes had been dropped.Claude's write-up
The documented
DecodingReaderloop (set_encoding(e.encoder())onEvent::Decl) panicked when the XML declaration did not fit in the 64-byte detection prefix:With non-ASCII content after the declaration, reading failed with a UTF-8 error before
Declwas even returned.Fix: when the prefix is used up before the UTF-8 decoder has seen a
>, read another 64-byte chunk into it instead of dropping it. The prefix then always ends after the declaration, so the existing "stop at the first>" logic from #1023 and theprefix.is_some()check inset_encodingwork unchanged. Only the declaration goes through the small prefix; the rest is decoded by the main path as before (input with no>at all is now read 64 bytes at a time).UTF-16 documents that declare
UTF-8: with a BOM the prefix holds 31 UTF-16 characters, so a complete declaration such as<?xml version="1.0" encoding="UTF-8"?>hit the same panic. A shorter one switched the decoder to the declared encoding and produced garbage.set_encodingnow ignores an ASCII-compatible encoding on a UTF-16 stream, since the declaration could only be read as UTF-16. The issue suggested returning an error instead; ignoring is one line and lets these documents parse. Switching to an error is easy if preferred.This does not cover the UTF-16LE/BE case from #1033 (neither encoding is ASCII-compatible). Both PRs edit the same
ifinset_encoding, so whichever lands second needs a small rebase.Tests:
declaration_longer_than_prefixandutf16_declared_as_utf8intests/encodings.rsfail on master (the first with the UTF-8 error, the second with the panic above) and pass with the fix. A unit test insrc/encoding.rschecks that only the declaration is read through the prefix.