From 6d26fbeeda2176b76ca1c5ebda0ee1b1b4e7459d Mon Sep 17 00:00:00 2001 From: Alex Helfet Date: Sat, 30 Dec 2017 17:21:25 +0000 Subject: [PATCH] Add checksum. --- Cargo.lock | 14 +++++++ framed/Cargo.toml | 2 + framed/src/bytes.rs | 92 +++++++++++++++++++++++++++++++++------------ framed/src/error.rs | 3 ++ framed/src/lib.rs | 12 ++++-- framed/src/typed.rs | 14 +++---- 6 files changed, 103 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b1b824..c069672 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,11 @@ name = "bitflags" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "case" version = "0.1.0" @@ -43,6 +48,11 @@ name = "cobs" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "crc16" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "csv" version = "1.0.0-beta.5" @@ -84,7 +94,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "framed" version = "0.2.0" dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cobs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crc16 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "ref_slice 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -307,9 +319,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" "checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum case 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e88b166b48e29667f5443df64df3c61dc07dc2b1a0b0d231800e07f09a33ecc1" "checksum clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "110d43e343eb29f4f51c1db31beb879d546db27998577e5715270a54bcf41d3f" "checksum cobs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "98acedfd5fc3ef93c4c844980f30318a445c24f76863a9e51f0599b9f1d274ad" +"checksum crc16 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "11a65c4797332f3e3a5945e0377875afc79b1bdc87082a4f98ac1ef15b47e2dd" "checksum csv 1.0.0-beta.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e7a9e063dcebdb56c306f23e672bfd31df3da8ec5f6d696b35f2c29c2a9572f0" "checksum csv-core 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1fbabf21d9a52d04675cc5b032d7bae24ecdcd22646f7eefcd0496a122686c" "checksum derive-error 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ec098440b29ea3b1ece3e641bac424c19cf996779b623c9e0f2171495425c2c8" diff --git a/framed/Cargo.toml b/framed/Cargo.toml index ac40707..775cd7a 100644 --- a/framed/Cargo.toml +++ b/framed/Cargo.toml @@ -14,7 +14,9 @@ repository = "fluffysquirrels/framed-rs" branch = "master" [dependencies] +byteorder = { version = "^1.2.1", default-features = false } cobs = "^0.1.3" +crc16 = "^0.3.4" ref_slice = "^1.1.1" serde = "^1.0" ssmarshal = "^1.0" diff --git a/framed/src/bytes.rs b/framed/src/bytes.rs index ab7e914..9943057 100644 --- a/framed/src/bytes.rs +++ b/framed/src/bytes.rs @@ -74,7 +74,9 @@ use ::{Payload, Encoded, FRAME_END_SYMBOL}; #[cfg(feature = "use_std")] use ::{BoxPayload, BoxEncoded}; use ::error::{Error, Result}; +use byteorder::{self, ByteOrder}; use cobs; +use crc16; #[cfg(feature = "use_std")] use ref_slice::ref_slice_mut; @@ -84,7 +86,18 @@ use std::io::{self, Read, Write}; const HEADER_LEN: usize = 0; -const FOOTER_LEN: usize = 1; +const CHECKSUM_LEN: usize = 2; + +const FOOTER_LEN: usize = CHECKSUM_LEN + 1; + +const FRAMING_LEN: usize = HEADER_LEN + FOOTER_LEN; + +fn checksum(p: &Payload) -> [u8; CHECKSUM_LEN] { + let u16 = crc16::State::::calculate(p); + let mut bytes = [0u8; CHECKSUM_LEN]; + byteorder::NetworkEndian::write_u16(&mut bytes, u16); + bytes +} /// Encode the supplied payload data as a frame at the beginning of /// the supplied buffer `dest`. Available from `no_std` crates. @@ -96,17 +109,29 @@ const FOOTER_LEN: usize = 1; /// This function will panic if `dest` is not large enough for the encoded frame. /// Ensure `dest.len() >= max_encoded_len(p.len())`. pub fn encode_to_slice(p: &Payload, dest: &mut Encoded) -> Result { - // Panic if code won't fit in `dest` because this is a programmer error. + // Panic if encoded frame won't fit in `dest` because this is a + // programmer error. assert!(max_encoded_len(p.len()) <= dest.len()); let cobs_len = cobs::encode(&p, &mut dest[HEADER_LEN..]); - let footer_idx = HEADER_LEN + cobs_len; - dest[footer_idx] = FRAME_END_SYMBOL; + let checksum = checksum(p); - #[cfg(feature = "trace")] { - println!("framed: Frame code = {:?}", &dest[0..(footer_idx + 1)]); + { + let mut _header = &mut dest[0..HEADER_LEN]; } - Ok(cobs_len + HEADER_LEN + FOOTER_LEN) + { + let footer = &mut dest[ + (HEADER_LEN + cobs_len) + .. + (HEADER_LEN + cobs_len + FOOTER_LEN)]; + footer[0..CHECKSUM_LEN].copy_from_slice(&checksum); + footer[CHECKSUM_LEN] = FRAME_END_SYMBOL; + } + let len = HEADER_LEN + cobs_len + FOOTER_LEN; + #[cfg(feature = "trace")] { + println!("framed: Encoded frame = {:?}", &dest[0..len]); + } + Ok(len) } /// Encode the supplied payload data as a frame and return it on the heap. @@ -167,6 +192,10 @@ pub fn decode_to_slice(e: &Encoded, dest: &mut [u8]) return Err(Error::EofBeforeFrame); } + if e.len() < FRAMING_LEN { + return Err(Error::EofDuringFrame); + } + if e[e.len()-1] != FRAME_END_SYMBOL { return Err(Error::EofDuringFrame) } @@ -174,19 +203,34 @@ pub fn decode_to_slice(e: &Encoded, dest: &mut [u8]) assert!(dest.len() >= max_decoded_len(e.len())); assert_eq!(e[e.len() - 1], FRAME_END_SYMBOL); - // Just the body (COBS-encoded payload). - let body = &e[0..(e.len()-1)]; - - let len = cobs::decode(body, dest) - .map_err(|_| Error::CobsDecodeFailed)?; + let _header = &e[0..HEADER_LEN]; + let body = &e[HEADER_LEN..(e.len() - FOOTER_LEN)]; + let footer = &e[(e.len() - FOOTER_LEN)..e.len()]; #[cfg(feature = "trace")] { - println!("framed: body = {:?}\n\ - framed: decoded = {:?}", - body, &dest[0..len]); + println!("framed: header = {:?}\n\ + framed: body = {:?}\n\ + framed: footer = {:?}", + _header, body, footer); } - Ok(len) + let decoded_len = cobs::decode(body, dest) + .map_err(|_| Error::CobsDecodeFailed)?; + + let decoded = &dest[0..decoded_len]; + let calc_checksum = checksum(decoded); + let received_checksum = &footer[0..CHECKSUM_LEN]; + + if calc_checksum != received_checksum { + return Err(Error::ChecksumError); + } + + #[cfg(feature = "trace")] { + println!("framed: decoded = {:?}", + decoded); + } + + Ok(decoded_len) } /// Decode the supplied encoded frame, returning the payload on the heap. @@ -372,11 +416,11 @@ mod tests { #[test] fn max_encoded_len_ok() { - assert_eq!(max_encoded_len(0) , 2); - assert_eq!(max_encoded_len(1) , 3); - assert_eq!(max_encoded_len(2) , 4); - assert_eq!(max_encoded_len(254), 257); - assert_eq!(max_encoded_len(255), 258); + assert_eq!(max_encoded_len(0) , 4); + assert_eq!(max_encoded_len(1) , 5); + assert_eq!(max_encoded_len(2) , 6); + assert_eq!(max_encoded_len(254), 259); + assert_eq!(max_encoded_len(255), 260); } #[test] @@ -501,7 +545,7 @@ mod rw_tests { fn one_frame() { let (mut tx, mut rx) = pair(); let p = [0x00, 0x01, 0x02]; - assert_eq!(tx.send(&p).unwrap(), 5); + tx.send(&p).unwrap(); let recvd = rx.recv().unwrap(); assert_eq!(*recvd, p); } @@ -511,14 +555,14 @@ mod rw_tests { let (mut tx, mut rx) = pair(); { let sent = [0x00, 0x01, 0x02]; - assert_eq!(tx.send(&sent).unwrap(), 5); + tx.send(&sent).unwrap(); let recvd = rx.recv().unwrap(); assert_eq!(*recvd, sent); } { let sent = [0x10, 0x11, 0x12]; - assert_eq!(tx.send(&sent).unwrap(), 5); + tx.send(&sent).unwrap(); let recvd = rx.recv().unwrap(); assert_eq!(*recvd, sent); } diff --git a/framed/src/error.rs b/framed/src/error.rs index f461104..c0aae13 100644 --- a/framed/src/error.rs +++ b/framed/src/error.rs @@ -19,6 +19,9 @@ pub enum Error { /// COBS decode failed CobsDecodeFailed, + /// Checksum error: the received frame was corrupted. + ChecksumError, + /// End of data while reading a frame; we received some of a frame /// but it was incomplete. EofDuringFrame, diff --git a/framed/src/lib.rs b/framed/src/lib.rs index 6b04ad3..2f79a24 100644 --- a/framed/src/lib.rs +++ b/framed/src/lib.rs @@ -22,8 +22,13 @@ //! //! Currently the encoding is: //! -//! * The "body": payload [COBS]-encoded to remove bytes equal to zero -//! * A terminating zero byte. +//! * Header: +//! * Nothing here yet. +//! * Body: payload [COBS]-encoded to remove bytes equal to zero +//! * Footer: +//! * A 2 byte checksum. +//! * A terminating zero byte. +//! //! [COBS]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing //! //! The encoding is not stable at the moment, i.e. it can and will @@ -171,13 +176,14 @@ macro_rules! const_fn { } // ## extern crate statements +extern crate byteorder; extern crate cobs; #[cfg(feature = "use_std")] extern crate core; +extern crate crc16; extern crate ref_slice; - extern crate serde; #[macro_use] diff --git a/framed/src/typed.rs b/framed/src/typed.rs index a030bbb..8a3d3f7 100644 --- a/framed/src/typed.rs +++ b/framed/src/typed.rs @@ -386,17 +386,17 @@ mod tests { #[test] fn serialize_buf_len() { - assert_eq!(max_serialize_buf_len::(), 42); - assert_eq!(max_serialize_buf_len::(), 3); - assert_eq!(max_serialize_buf_len::(), 4); - assert_eq!(max_serialize_buf_len::(), 6); - assert_eq!(max_serialize_buf_len::(), 10); + assert_eq!(max_serialize_buf_len::(), 44); + assert_eq!(max_serialize_buf_len::(), 5); + assert_eq!(max_serialize_buf_len::(), 6); + assert_eq!(max_serialize_buf_len::(), 8); + assert_eq!(max_serialize_buf_len::(), 12); } #[test] fn encoded_len() { - assert_eq!(max_encoded_len::(), 44); - assert_eq!(max_encoded_len::(), 5); + assert_eq!(max_encoded_len::(), 48); + assert_eq!(max_encoded_len::(), 9); } }