Skip to content

Fix frame parsing when the ring buffer wraps around - #37

Open
Shumon Fujita (shumon84) wants to merge 1 commit into
mainfrom
fix/ring-buffer-wraparound
Open

Shumon Fujita (shumon84) wants to merge 1 commit into
mainfrom
fix/ring-buffer-wraparound

Conversation

@shumon84

Copy link
Copy Markdown

Problem

Parser drops valid frames whenever a frame wraps around the end of its ring buffer (VecDeque / heapless::Deque), because Buffer::data() then returns the bytes as two slices and two helpers mishandle that case:

  • slices_endx drops the first slice. When the end index falls into the second slice it returns (&[], &b[..endx - a.len()]) instead of (a, &b[..endx - a.len()]). A body that spans the end of the buffer loses its head, so the CRC check fails and the frame is reported as InvalidCrc junk. (get_body uses the same helper, so the body would also be truncated.)
  • find_stx only checks a spanning STX when the first slice has an odd length (buf_a.len() & 1 == 1). When EB is the last byte of the first slice and 90 is the first byte of the second, the STX is missed for even lengths and the frame is skipped as InvalidStx junk.

The existing tests never wrap the buffer (each starts writing at index 0), so neither case was covered.

Impact

kble-eb90 decode (kble v0.4.2, 2048-byte buffer) loses about 1 in 5 frames from a C2A PCDU that sends 431-byte EB90 frames: every frame that wraps around the buffer is dropped as InvalidCrc. A larger --buffer-size (the kble v0.5.0 default) makes the buffer wrap less often, but frames are still lost each time it does.

Fix

  • slices_endx: return (a, &b[..endx - a.len()]).
  • find_stx: check the spanning STX regardless of the first slice's length.

Test

test_wraparound shifts the buffer's write position one byte at a time, so the frame wraps at every possible position. It also puts 0 to 4 junk bytes before the STX, so that an STX spanning the two slices occurs with both odd and even first-slice lengths.

  • Before the fix: fails at lead=0 offset=9 (slices_endx).
  • With only the slices_endx fix: fails at lead=1 offset=14 (find_stx).
  • With both fixes: all tests pass.

I also replayed a 30-second capture of the PCDU's serial output, split into the same chunks kble-serialport sent over WebSocket, through Decoder. With a 2048-byte buffer, 0.1.1 decodes 26 of 30 frames; this branch decodes 30 of 30. All 30 frames in the capture have valid CRCs.

Note on CI

The Rust workflow fails on main regardless of this change: with the pinned toolchain 1.70.0, cargo cannot resolve the current dependencies (getrandom v0.4.3 via proptest uses edition 2024). #34 (Rust 1.99.1) passes CI, so this PR should go green once #34 is merged. I ran the tests, cargo fmt --check and clippy locally with stable. Clippy's only remaining warning (mismatched_lifetime_syntaxes on Parser::read) is in existing code and is not touched here.

🤖 Generated with Claude Code

- slices_endx dropped the first slice when the end index fell into the
  second one, so a body spanning the end of the buffer lost its head.
  The CRC check then failed and the frame was reported as InvalidCrc junk.
- find_stx only looked for an STX spanning the two slices when the first
  slice had an odd length, so it missed the STX otherwise and the frame
  was skipped as InvalidStx junk.

Add a test that shifts the write position of the buffer by one byte at a
time so that the frame wraps around at every possible position.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant