fix packet scan - #73
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens the streaming SBF parser against pathological “false sync” cases where noise contains the $@ sync bytes and a plausible length, which previously could prevent the parser from ever reaching the next real message. The new approach scans forward for the next complete and CRC-valid message (while retaining any plausible partial candidate), improving robustness and reducing unnecessary rescanning on noisy inputs.
Changes:
- Reworked
parse_messageto scan forward for the next valid complete message and return an appropriate drain amount, avoiding stalls on false syncs with oversized/incomplete bodies. - Refactored payload decoding into
decode_bodyand simplifiedSbfParser::consumeto a single pass per call. - Added unit tests to cover false-sync and partial-message buffering scenarios; updated proptest regression corpus.
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/parser.rs | Implements forward-scanning resync logic for streaming parsing and adds regression/unit tests to validate behavior under noise/false sync conditions. |
| proptest-regressions/parser.txt | Adds a new proptest regression seed/case to preserve the discovered pathological input. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
74a1ea8 to
ac45db4
Compare
|
I ran the proptest-regression from this PR against the current parser implementation and it doesn't fail. Do you mean if the noise contains the sync bits it drops a message? We do clean up the noise data here to avoid having impossible message lengths in the proptest. |
ac45db4 to
e132953
Compare
e132953 to
33a09ef
Compare
33a09ef to
b12c28e
Compare
| assert!(matches!(result, Err(DatagramError::InvalidCrc))); | ||
| } | ||
|
|
||
| /// A false `$@` whose length field claims more bytes than the buffer holds |
There was a problem hiding this comment.
These two unit tests create 2 contrived scenarios that are also impossible to generate from the proptest:
- The proptest has a sanitize noise function which sets the length of a fake sync to 1 so it would have been impossible to ever see these two scenarios which is why I probably wasn't able to repro it.
- The one test you have that is worth digging more into is the one with a very large length. It is technically legal for the length field in SBF to be an arbitrary u16 so we should scan the whole input for that value. The old parser will correctly keep returning
IncompleteDatauntil it scans the full1000bytes and then it will unpack that data and return it as anInvalidCRCif the message isn't a valid SBF type or if the message is valid then it would returnUnsupported. If anInvalidCRCis received we still have the old buffer and will start scanning for a new fake sync so no message is dropped. This unit test is not actually testing this though. Instead, this unit test does the following:- Passes a buffer with a fake-sync + large-header + sync + valid-message
- The sync + valid-message size is shorter than the 1000 byte length in the fake-sync
- The parser will get to the end of the buffer and return None since there is no more data
- This scenario wouldn't happen in practice with a streaming parser since no more data coming would mean the connection to the Septentrio some how ended (e.g network failure) and that would be an error further up in the buffered reader.
I don't think this parser re-write is necessary on the basis of just correctness.
There was a problem hiding this comment.
Sounds good. I'll close this PR for now. Will investigate the longer buffer size behavior when I have the time.
Running proptests on another branch, I found this pathological condition where the parser fails to find correct messages if garbage contains the expected init sequence, even if nothing else matches up.
Two unit tests added to catch regression.
Did a throughput test on some noisy data: this actually makes the parser faster since less re-scanning needs to happen. No real timing differences on non-noisy data in mega-sbf file.
Unit tests on
main: