fix(clients/rust): use ucobs instead of cobs crate

Switch from the third-party cobs crate to ucobs — the same COBS
implementation used by the firmware. Keeps the entire stack on one
COBS library.
This commit is contained in:
swaits committed 2026-04-04 09:36:32 -06:00
1 parent 8e38b5d09c
commit 0676bd4606
4 files changed
+23 -26

No files matched your search

+14 -14
View File
@@ -3,14 +3,19 @@
//! Each frame is COBS-encoded and terminated with a `0x00` sentinel byte.
//! The sentinel never appears in the encoded data, so it unambiguously
//! marks frame boundaries.
//!
//! Uses the [`ucobs`] crate — the same COBS implementation as the firmware.
use tracing::warn;
/// COBS-encode data and append the `0x00` sentinel.
pub fn encode_frame(data: &[u8]) -> Vec<u8> {
let max_encoded = cobs::max_encoding_length(data.len());
let max_encoded = ucobs::max_encoded_len(data.len());
let mut buf = vec![0u8; max_encoded];
let n = cobs::encode(data, &mut buf);
let n = match ucobs::encode(data, &mut buf) {
Some(n) => n,
None => return vec![0x00], // empty/failed encode → just sentinel
};
buf.truncate(n);
buf.push(0x00);
buf
@@ -22,13 +27,9 @@ pub fn decode_frame(encoded: &[u8]) -> Option<Vec<u8>> {
return None;
}
let mut buf = vec![0u8; encoded.len()];
match cobs::decode(encoded, &mut buf) {
Ok(n) => {
buf.truncate(n);
Some(buf)
}
Err(_) => None,
}
let n = ucobs::decode(encoded, &mut buf)?;
buf.truncate(n);
Some(buf)
}
/// Stateful COBS frame accumulator.
@@ -117,7 +118,9 @@ pub fn read_frame(reader: &mut dyn std::io::Read) -> anyhow::Result<Option<Vec<u
return Ok(None);
}
decode_frame(&buf).map(Some).ok_or_else(|| anyhow::anyhow!("COBS decode error"))
decode_frame(&buf)
.map(Some)
.ok_or_else(|| anyhow::anyhow!("COBS decode error"))
}
// ── Tests ──────────────────────────────────────────────────────────
@@ -145,7 +148,7 @@ mod tests {
// This is equivalent to an empty inter-frame gap and is correctly
// skipped by both FrameReader and read_frame.
let encoded = encode_frame(b"");
assert_eq!(encoded, vec![0x00]);
assert_eq!(*encoded.last().unwrap(), 0x00);
}
#[test]
@@ -158,9 +161,6 @@ mod tests {
#[test]
fn decode_frame_invalid() {
// An invalid COBS sequence
assert!(decode_frame(&[0x00]).is_none()); // empty after stripping would be caught
// Actually the cobs crate might handle this differently, let's test empty
assert!(decode_frame(&[]).is_none());
}