diff --git a/Cargo.toml b/Cargo.toml index 5196341..7bf1d12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,11 @@ repository = "https://github.com/fluffysquirrels/framed" [dependencies] cobs = "^0.1.2" -error-chain = "^0.11.0" \ No newline at end of file +error-chain = "^0.11.0" +ref_slice = "^1.1.1" + +[features] +default_features = [] + +# Enable to print all data to stdout for testing. +trace = [] \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index 49c3507..5d72325 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,5 +12,9 @@ error_chain! { description("COBS decode failed"), display("COBS decode failed"), } + EofDuringFrame { + description("end of file during a frame"), + display("end of file during a frame"), + } } } diff --git a/src/lib.rs b/src/lib.rs index a109c72..4c36f7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,19 @@ //! //! [dll]: https://en.wikipedia.org/wiki/Data_link_layer //! [pl]: https://en.wikipedia.org/wiki/Physical_layer - -// TODO: Make this work when no_std. +//! +//! Currently the encoding is: the frame [COBS]-encoded +//! to remove bytes equal to zero, then a terminating zero byte. +//! [COBS]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing +//! +//! ## Cargo feature flags +//! `trace`: Enable to print all data to stdout for testing. +//! +//! ## TODO +//! * Start frame with a zero as well, then we detect partial frames. +//! * Add a length field to the beginning of the frame and a +//! checksum to the end. Perhaps make them optional. +//! * Support no_std. #![deny(warnings)] #![feature(conservative_impl_trait)] @@ -17,12 +28,16 @@ extern crate cobs; #[macro_use] extern crate error_chain; +extern crate ref_slice; pub mod channel; pub mod error; use error::{Error, ErrorKind, Result}; -use std::io::{Read, Write}; +use ref_slice::ref_slice_mut; +use std::io::{self, Read, Write}; + +const FRAME_END: u8 = 0; /// Sends frames over an underlying `io::Write` instance. pub struct Sender { @@ -37,7 +52,11 @@ impl Sender { } pub fn send(&mut self, f: &[u8]) -> Result<()> { - let code = cobs::encode_vec(f); + let mut code = cobs::encode_vec(f); + code.push(FRAME_END); + #[cfg(feature = "trace")] { + println!("framed: Sending code = {:?}", code); + } self.w.write(&code)?; Ok(()) } @@ -45,6 +64,7 @@ impl Sender { /// Receives frames from an underlying `io::Read` instance. pub struct Receiver { + /// The underlying reader r: R, } @@ -56,9 +76,35 @@ impl Receiver { } pub fn recv(&mut self) -> Result> { - let mut code = Vec::new(); - self.r.read_to_end(&mut code)?; - cobs::decode_vec(&code) + let mut next_frame = Vec::new(); + + let mut b = 0u8; + loop { + let res = self.r.read(ref_slice_mut(&mut b)); + #[cfg(feature = "trace")] { + println!("framed: Read result = {:?}", res); + } + match res { + Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => + return Err(Error::from(ErrorKind::EofDuringFrame)), + Ok(0) => + return Err(Error::from(ErrorKind::EofDuringFrame)), + Err(e) => return Err(Error::from(e)), + Ok(_) => (), + }; + + #[cfg(feature = "trace")] { + println!("framed: Read byte = {}", b); + } + if b == FRAME_END { + break; + } else { + next_frame.push(b); + } + } + assert!(b == FRAME_END); + + cobs::decode_vec(&next_frame) .map_err(|_| Error::from(ErrorKind::CobsDecodeFailed)) } } @@ -66,6 +112,7 @@ impl Receiver { #[cfg(test)] mod tests { use channel::Channel; + use error::{Error, ErrorKind}; use std::io::{Read, Write}; use super::{Receiver, Sender}; @@ -78,13 +125,50 @@ mod tests { assert_eq!(recvd, sent); } - // TODO: Test sending 2 different frames. + #[test] + fn two_frames_sequentially() { + let (mut tx, mut rx) = pair(); + { + let sent = [0x00, 0x01, 0x02]; + tx.send(&sent).unwrap(); + let recvd = rx.recv().unwrap(); + assert_eq!(recvd, sent); + } - // TODO: Test buffering: - // * Write half a frame - // * recv() returns nothing - // * Write the rest of the frame - // * recv() returns the whole frame + { + let sent = [0x10, 0x11, 0x12]; + tx.send(&sent).unwrap(); + let recvd = rx.recv().unwrap(); + assert_eq!(recvd, sent); + } + } + + #[test] + fn two_frames_at_once() { + let (mut tx, mut rx) = pair(); + let s1 = [0x00, 0x01, 0x02]; + let s2 = [0x10, 0x11, 0x12]; + + tx.send(&s1).unwrap(); + tx.send(&s2).unwrap(); + + let r1 = rx.recv().unwrap(); + let r2 = rx.recv().unwrap(); + println!("r1: {:?}\n\ + r2: {:?}", r1, r2); + + assert_eq!(r1, s1); + assert_eq!(r2, s2); + } + + #[test] + fn empty_input() { + let (mut _tx, mut rx) = pair(); + match rx.recv() { + Err(Error(ErrorKind::EofDuringFrame, _)) => (), + e @ _ => panic!("Bad value: {:?}", e) + } + } fn pair() -> (Sender, Receiver) { let c = Channel::new();