//! Send and receive data over lossy streams of bytes. //! //! Living in / inspired by the [data link layer][dll] or layer 2 //! in the OSI networking model, this module enables sending slices of //! bytes of definite length over an underlying lossy transport that //! only supports sending an unstructured stream of bytes (a [physical //! layer][pl], such as ITM, UART or SPI). //! //! [dll]: https://en.wikipedia.org/wiki/Data_link_layer //! [pl]: https://en.wikipedia.org/wiki/Physical_layer //! //! The transport may corrupt the stream by dropping or modifying some //! bytes en route. When the transport returns corrupt data the //! decoder may return errors or corrupted payloads, but if the //! transport starts operating without losses again the decoder should //! return new uncorrupted frames. //! //! ## Encoding //! //! Currently the encoding is: //! //! * The "body": payload [COBS]-encoded to remove bytes equal to zero //! * 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 //! change between minor versions. Consequently encoded data from this //! crate is unsuitable for long-term storage or transmission between //! different versions of an application. The API should be kept //! stable between versions where possible and the crate version will //! follow Rust semver rules on API changes. //! //! ## Cargo feature flags //! //! `use_std`: Use standard library. Enabled by default, disable for no_std. //! //! `trace`: Enable to print all data to stdout for testing. //! //! `typed`: Enables the [`typed`](typed/index.html) sub-module for sending and //! receiving structs serialized with serde. Enabled by default. //! //! ## API //! //! Payload data and encoded frame data have separate types to avoid //! mixing them up. You are encouraged to use these types in your own //! code when integrating with this crate. Definitions: //! //! ```rust,ignore //! /// Arbitrary user data. //! pub type Payload = [u8]; //! //! /// Data that is encoded as a frame. It is ready to send, or may have //! /// just been received. //! pub type Encoded = [u8]; //! //! /// A buffer that is used as temporary storage. //! /// There are no guarantees on its contents after use. //! pub type TempBuffer = [u8]; //! //! /// Heap-allocated user data used as a return type. //! #[cfg(feature = "use_std")] //! pub struct BoxPayload(_); //! //! /// Heap-allocated frame data used as a return type. //! #[cfg(feature = "use_std")] //! pub struct BoxEncoded(_); //! ``` //! //! Consumers have a choice of interfaces to enable usability, //! efficiency, and use from `no_std` crates. //! //! See the `decode_*` and `encode_*` functions for simple uses with //! various input and output types. //! //! Note that data typed as `Payload` or `Encoded` may be //! efficiently passed as a function argument by reference, but is //! returned using an opaque struct (`BoxPayload`, `BoxEncoded`) //! containing a heap-allocated value instead. Consequently `encode_*` //! and `decode_*` variants that require this are only available with //! the `use_std` Cargo feature. //! //! For sending or receiving a stream of frames, consider the `Reader` //! and `Writer` structs that wrap an `io::Read` or `io::Write` //! instance. //! //! ## Example usage from a std crate //! //! TODO. //! //! ## Example usage from a no_std crate //! //! TODO. //! #![deny(warnings)] #![cfg_attr(not(feature = "use_std"), no_std)] // TODO: Disable this when toolchain != nightly. #![feature(const_fn)] // ## extern crate statements extern crate cobs; #[cfg(feature = "use_std")] extern crate core; extern crate ref_slice; #[cfg(feature = "typed")] extern crate serde; #[macro_use] #[cfg(all(test, feature = "typed", feature = "use_std"))] extern crate serde_derive; #[cfg(feature = "typed")] extern crate ssmarshal; // ## Sub-modules #[cfg(feature = "use_std")] pub mod channel; pub mod error; pub use error::{Error, Result}; #[cfg(all(feature = "typed", feature = "use_std"))] pub mod typed; // ## use statements #[cfg(feature = "use_std")] use ref_slice::ref_slice_mut; #[cfg(feature = "use_std")] use std::io::{self, Read, Write}; #[cfg(feature = "use_std")] use std::ops::Deref; /// Arbitrary user data. pub type Payload = [u8]; /// Data that is encoded as a frame. It is ready to send, or may have /// just been received. pub type Encoded = [u8]; /// A buffer that is used as temporary storage. /// There are no guarantees on its contents after use. pub type TempBuffer = [u8]; // Note: BoxPayload and BoxEncoded store data in a Vec as that's // what `cobs` returns us and converting them into a Box<[u8]> (with // Vec::into_boxed_slice(self)) would require re-allocation. // // See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_boxed_slice /// Heap-allocated user data used as a return type. #[cfg(feature = "use_std")] #[derive(Debug)] pub struct BoxPayload(Vec); #[cfg(feature = "use_std")] impl From> for BoxPayload { fn from(v: Vec) -> BoxPayload { BoxPayload(v) } } #[cfg(feature = "use_std")] impl Deref for BoxPayload { type Target = [u8]; fn deref(&self) -> &[u8] { &*self.0 } } /// Heap-allocated frame data used as a return type. #[cfg(feature = "use_std")] #[derive(Debug)] pub struct BoxEncoded(Vec); #[cfg(feature = "use_std")] impl From> for BoxEncoded { fn from(v: Vec) -> BoxEncoded { BoxEncoded(v) } } #[cfg(feature = "use_std")] impl Deref for BoxEncoded { type Target = [u8]; fn deref(&self) -> &[u8] { &*self.0 } } /// The frame ends with (and includes) this byte. /// /// Consumers can read encoded data into a buffer until they encounter /// this value and then use one of the `decode_*` functions to decode /// the frame's payload. pub const FRAME_END_SYMBOL: u8 = 0; const HEADER_LEN: usize = 0; const FOOTER_LEN: usize = 1; /// Encode the supplied payload data as a frame at the beginning of /// the supplied buffer `dest`. /// /// Returns the number of bytes it has written to the buffer. /// /// # Panics /// /// 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. 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; #[cfg(feature = "trace")] { println!("framed: Frame code = {:?}", &dest[0..(footer_idx + 1)]); } Ok(cobs_len + HEADER_LEN + FOOTER_LEN) } /// Encode the supplied payload data as a frame and return it on the heap. #[cfg(feature = "use_std")] pub fn encode_to_box(p: &Payload) -> Result { let mut buf = vec![0; max_encoded_len(p.len())]; let len = encode_to_slice(p, &mut *buf)?; buf.truncate(len); Ok(BoxEncoded::from(buf)) } /// Encode the supplied payload data as a frame and write it to the /// supplied `Write`. /// /// This function will not call `flush` on the writer; the caller do /// so if this is required. /// /// Returns the length of the frame it has written. #[cfg(feature = "use_std")] pub fn encode_to_writer(p: &Payload, w: &mut W) -> Result { let b = encode_to_box(p)?; w.write_all(&*b.0)?; Ok(b.len()) } /// Decode the supplied encoded frame, placing the payload at the /// beginning of the supplied buffer `dest`. /// /// When reading from a stream, the caller can continue reading data /// and buffering it until a `FRAME_END_SYMBOL` is read, then pass the /// whole buffer including `FRAME_END_SYMBOL` to this function for /// decoding. /// /// If there is more than 1 FRAME_END_SYMBOL within `e`, the result /// is undefined. Make sure you only pass 1 frame at a time. /// /// Returns the length of the payload it has decoded. /// /// # Errors /// /// Returns `Err(Error::EofDuringFrame`) if `e` contains >= 1 bytes of /// a frame, but not a complete frame. A complete frame should have /// `FRAME_END_SYMBOL` as the last byte. /// /// Returns `Err(Error::EofBeforeFrame`) if `e.len()` is 0. /// /// # Panics /// /// This function will panic if `dest` is not large enough for the decoded frame. /// Ensure `dest.len() >= max_decoded_len(e.len())?`. pub fn decode_to_slice(e: &Encoded, mut dest: &mut [u8]) -> Result { #[cfg(feature = "trace")] { println!("framed: Encoded input = {:?}", e); } if e.len() == 0 { return Err(Error::EofBeforeFrame); } if e[e.len()-1] != FRAME_END_SYMBOL { return Err(Error::EofDuringFrame) } 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, &mut dest) .map_err(|_| Error::CobsDecodeFailed)?; #[cfg(feature = "trace")] { println!("framed: body = {:?}\n\ framed: decoded = {:?}", body, &dest[0..len]); } Ok(len) } /// Decode the supplied encoded frame, returning the payload on the heap. #[cfg(feature = "use_std")] pub fn decode_to_box(e: &Encoded) -> Result { if e.len() == 0 { return Err(Error::EofBeforeFrame); } let mut buf = vec![0; max_decoded_len(e.len())?]; let len = decode_to_slice(e, &mut buf)?; buf.truncate(len); Ok(BoxPayload::from(buf)) } /// Reads bytes from the supplied `Read` until it has a complete /// encoded frame, then decodes the frame, returning the payload on the heap. #[cfg(feature = "use_std")] pub fn decode_from_reader(r: &mut Read) -> Result { // Read until FRAME_END_SYMBOL let mut next_frame = Vec::new(); let mut b = 0u8; loop { let res = r.read(ref_slice_mut(&mut b)); #[cfg(feature = "trace")] { println!("framed: Read result = {:?}", res); } match res { // In the 2 EOF cases defer to decode_to_box to return the // correct error (EofBeforeFrame or EofDuringFrame). Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => return decode_to_box(&*next_frame), Ok(0) => return decode_to_box(&*next_frame), Err(e) => return Err(Error::from(e)), Ok(1) => (), Ok(_) => unreachable!(), }; #[cfg(feature = "trace")] { println!("framed: Read byte = {}", b); } next_frame.push(b); if b == FRAME_END_SYMBOL { break; } } assert_eq!(next_frame[next_frame.len()-1], FRAME_END_SYMBOL); decode_to_box(&*next_frame) } /// Returns an upper bound for the decoded length of the payload /// within a frame with the encoded length supplied. /// /// Useful for calculating an appropriate buffer length. pub fn max_decoded_len(code_len: usize) -> Result { let framing_len = HEADER_LEN + FOOTER_LEN; if code_len < framing_len { return Err(Error::EncodedFrameTooShort) } let cobs_len = code_len - framing_len; // If every byte is a 0x00, then COBS-encoded data will be the // same length of 0x01. let cobs_decode_limit = cobs_len; Ok(cobs_decode_limit) } /// Returns an upper bound for the encoded length of a frame with /// the payload length supplied. /// /// Useful for calculating an appropriate buffer length. pub const fn max_encoded_len(payload_len: usize) -> usize { HEADER_LEN + cobs_max_encoded_len(payload_len) + FOOTER_LEN } /// Copied from `cobs` crate to make a `const` version. /// /// Source: https://github.com/awelkie/cobs.rs/blob/f8ff1ad2aa7cd069a924d75170d3def3fa6df10b/src/lib.rs#L183-L188 /// /// TODO: Submit a PR to `cobs` to make `cobs::max_encoding_length` a `const fn`. /// Issue for this: https://github.com/fluffysquirrels/framed-rs/issues/19 const fn cobs_max_encoded_len(payload_len: usize) -> usize { payload_len + (payload_len / 254) // This `+ 1` was // `+ if payload_len % 254 > 0 { 1 } else { 0 }` in cobs.rs, // but that won't compile in a const fn. `1` is less than both the // values in the if and else branches, so use that instead, with the // acceptable cost of allocating 1 byte more than required some of the // time. // // const fn compiler error was: // ``` // error[E0019]: constant function contains unimplemented expression type // --> framed/src/lib.rs:388:11 // | // 388 | + if payload_len % 254 > 0 { 1 } else { 0 } // | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // // error: aborting due to previous error // ``` // // Relevant section of const fn design doc: // https://github.com/rust-lang/rfcs/blob/5f69ff50de1fb6d0dd8c005b4f11f6e436e1f34c/text/0911-const-fn.md#detailed-design // const fn tracking issue: https://github.com/rust-lang/rust/issues/24111 + 1 } /// Sends encoded frames over an inner `io::Write` instance. #[cfg(feature = "use_std")] pub struct Sender { w: W, } #[cfg(feature = "use_std")] impl Sender { /// Construct a `Sender` that sends frames over the supplied /// `io::Write`. pub fn new(w: W) -> Sender { Sender:: { w: w, } } /// Consume this `Sender` and return the inner `io::Write`. pub fn into_inner(self) -> W { self.w } /// Flush all buffered data. Includes calling `flush` on the inner /// writer. pub fn flush(&mut self) -> Result<()> { Ok(self.w.flush()?) } /// Queue the supplied payload for transmission. /// /// This `Sender` may buffer the data indefinitely, as may the /// inner writer. To ensure all buffered data has been /// transmitted call [`flush`](#method.flush). /// /// See also: [`send`](#method.send) pub fn queue(&mut self, p: &Payload) -> Result { encode_to_writer(p, &mut self.w) } /// Encode the supplied payload as a frame, write it to the /// inner writer, then flush. /// /// Ensures the data has been transmitted before returning to the /// caller. /// /// See also: [`queue`](#method.queue) pub fn send(&mut self, p: &Payload) -> Result { let len = self.queue(p)?; self.flush()?; Ok(len) } } /// Receives encoded frames from an inner `io::Read` instance. #[cfg(feature = "use_std")] pub struct Receiver { r: R, } #[cfg(feature = "use_std")] impl Receiver { /// Construct a `Receiver` that receives frames from the supplied /// `io::Read`. pub fn new(r: R) -> Receiver { Receiver:: { r: r, } } /// Consume this `Receiver` and return the inner `io::Read`. pub fn into_inner(self) -> R { self.r } /// Receive an encoded frame from the inner `io::Read`, decode it /// and return the payload. pub fn recv(&mut self) -> Result { decode_from_reader::(&mut self.r) } } #[cfg(test)] mod tests { use super::*; #[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); } #[test] fn max_decoded_len_too_short() { match max_decoded_len(0) { Err(Error::EncodedFrameTooShort) => (), e @ _ => panic!("Bad output: {:?}", e) } } #[test] fn max_decoded_len_ok() { assert_eq!(max_decoded_len(1) .unwrap(), 0); assert_eq!(max_decoded_len(2) .unwrap(), 1); assert_eq!(max_decoded_len(3) .unwrap(), 2); assert_eq!(max_decoded_len(255).unwrap(), 254); } } // TODO: Add tests for all encode_*, decode_* functions. #[cfg(all(test, feature = "use_std"))] mod rw_tests { use channel::Channel; use error::Error; use std::io::{Read, Write}; use super::*; #[test] fn one_frame() { let (mut tx, mut rx) = pair(); let p = [0x00, 0x01, 0x02]; assert_eq!(tx.send(&p).unwrap(), 5); let recvd = rx.recv().unwrap(); assert_eq!(*recvd, p); } #[test] fn two_frames_sequentially() { let (mut tx, mut rx) = pair(); { let sent = [0x00, 0x01, 0x02]; assert_eq!(tx.send(&sent).unwrap(), 5); let recvd = rx.recv().unwrap(); assert_eq!(*recvd, sent); } { let sent = [0x10, 0x11, 0x12]; assert_eq!(tx.send(&sent).unwrap(), 5); 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::EofBeforeFrame) => (), e @ _ => panic!("Bad value: {:?}", e) } } #[test] fn partial_input() { let c = Channel::new(); let mut rx = Receiver::new(Box::new(c.reader()) as Box); let mut tx_raw = c.writer(); tx_raw.write(&[0x01]).unwrap(); match rx.recv() { Err(Error::EofDuringFrame) => (), e @ _ => panic!("Bad value: {:?}", e) } } fn pair() -> (Sender>, Receiver>) { let c = Channel::new(); let tx = Sender::new(Box::new(c.writer()) as Box); let rx = Receiver::new(Box::new(c.reader()) as Box); (tx, rx) } }