diff --git a/decode/src/main.rs b/decode/src/main.rs index d1bb2e8..0413fde 100644 --- a/decode/src/main.rs +++ b/decode/src/main.rs @@ -14,7 +14,6 @@ mod error; use error::{Error, Result}; use clap::Arg; -use framed::typed::Receiver; use std::io::{stdin, stdout}; arg_enum! { @@ -47,7 +46,9 @@ fn try() -> Result<()> { let matches = app.get_matches(); let out_fmt = value_t!(matches, "out-format", OutputFormat)?; - let mut r = Receiver::<_, UserType>::new(stdin()); + let mut r = framed::bytes::Config::default() + .typed::() + .into_receiver(stdin()); let mut csvw: Option> = match out_fmt { diff --git a/framed/Cargo.toml b/framed/Cargo.toml index b2c2e84..795d212 100644 --- a/framed/Cargo.toml +++ b/framed/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "framed" +# Next version is 0.4.0 after moving functions onto Codec structs. version = "0.3.0" description = "Send and receive data over lossy streams of bytes." authors = ["Alex Helfet "] diff --git a/framed/src/bytes.rs b/framed/src/bytes.rs index 6caf3b7..74d9f0d 100644 --- a/framed/src/bytes.rs +++ b/framed/src/bytes.rs @@ -14,17 +14,19 @@ //! # use framed::bytes::*; //! # use std::io::Cursor; //! # +//! let config = Config::default(); +//! //! let payload = [1, 2, 3]; //! //! let mut encoded = vec![]; //! { -//! let mut sender = Sender::new(&mut encoded); +//! let mut sender = config.clone().into_sender(&mut encoded); //! sender.send(&payload).expect("send ok"); //! } //! //! // `encoded` now contains the encoded frame. //! -//! let mut receiver = Receiver::new(Cursor::new(encoded)); +//! let mut receiver = config.clone().into_receiver(Cursor::new(encoded)); //! let decoded = receiver.recv().expect("recv ok"); //! //! assert_eq!(payload, *decoded); @@ -33,13 +35,15 @@ //! ## Example usage from a no_std crate //! //! The `encode_to_slice` and `decode_from_slice` functions offer an -//! API for `no_std` crates that might not have a heap allocator +//! API for `no_std` crates that do not have a heap allocator //! available and cannot use `std::io::Read` or `std::io::Write`. //! //! ```rust //! # use framed::*; //! # use framed::bytes::*; //! # +//! let mut codec = Config::default().into_codec(); +//! //! // In a no_std crate without dynamic memory allocation we would typically //! // know the maximum payload length, which we can use for payload buffers. //! const MAX_PAYLOAD_LEN: usize = 10; @@ -56,13 +60,15 @@ //! assert!(payload.len() <= MAX_PAYLOAD_LEN); //! //! let mut encoded_buf = [0u8; MAX_FRAME_LEN]; -//! let encoded_len = encode_to_slice(&payload, &mut encoded_buf).expect("encode ok"); +//! let encoded_len = codec.encode_to_slice(&payload, &mut encoded_buf) +//! .expect("encode ok"); //! let encoded = &encoded_buf[0..encoded_len]; //! //! // `encoded` now contains the encoded frame. //! //! let mut decoded_buf = [0u8; MAX_PAYLOAD_LEN]; -//! let decoded_len = decode_to_slice(&encoded, &mut decoded_buf).expect("decode ok"); +//! let decoded_len = codec.decode_to_slice(&encoded, &mut decoded_buf) +//! .expect("decode ok"); //! let decoded = &decoded_buf[0..decoded_len]; //! //! assert_eq!(payload, *decoded); @@ -73,10 +79,12 @@ use ::{Payload, Encoded, FRAME_END_SYMBOL}; #[cfg(feature = "use_std")] use ::{BoxPayload, BoxEncoded}; +use ::checksum::MAX_CHECKSUM_LEN; use ::error::{Error, Result}; -use byteorder::{self, ByteOrder}; +use ::typed; use cobs; -use crc16; +use serde::Serialize; +use serde::de::DeserializeOwned; #[cfg(feature = "use_std")] use ref_slice::ref_slice_mut; @@ -84,222 +92,301 @@ use ref_slice::ref_slice_mut; #[cfg(feature = "use_std")] use std::io::{self, Read, Write}; -const HEADER_LEN: usize = 0; +pub use ::checksum::Checksum; -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 +/// Contains methods for encoding and decoding byte slices with a +/// specific configuration. +/// +/// Construct an instance from a `Config` instance with the +/// `Config::into_codec` method. +pub struct Codec { + config: Config } -/// Encode the supplied payload data as a frame at the beginning of -/// the supplied buffer `dest`. Available from `no_std` crates. +/// Configurable options for encoding and decoding byte slices using a +/// builder pattern. /// -/// 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 encoded frame won't fit in `dest` because this is a - // programmer error. - assert!(max_encoded_len(p.len()) <= dest.len()); +/// Construct an instance with `Config::default()`. +#[derive(Clone, Debug, Default)] +pub struct Config { + checksum: Checksum, +} - #[cfg(feature = "trace")] { - println!("framed::encode: Payload = {:?}", p); +impl Config { + /// Construct a `Codec` instance with this configuration. + pub fn into_codec(self) -> Codec { + Codec { + config: self, + } } - let cobs_len = cobs::encode(&p, &mut dest[HEADER_LEN..]); - let checksum = checksum(p); + #[cfg(feature = "use_std")] + /// Construct a `Receiver` instance with this configuration. + pub fn into_receiver(self, r: R) -> Receiver { + Receiver:: { + codec: self.into_codec(), + r: r, + } + } - { - let mut _header = &mut dest[0..HEADER_LEN]; + #[cfg(feature = "use_std")] + /// Construct a `Sender` instance with this configuration. + pub fn into_sender(self, w: W) -> Sender { + Sender:: { + codec: self.into_codec(), + w: w, + } + } + + /// Construct a `framed::typed::Config` instance to encode and decode a + /// serializable type `T` with this byte encoding configuration. + pub fn typed(self) -> typed::Config { + typed::Config::::new(self) + } + + /// Get the current checksum configuration. + pub fn checksum(&self) -> &Checksum { + &self.checksum + } + + /// Set the checksum configuration. + pub fn set_checksum(&mut self, checksum: Checksum) -> &mut Self { + self.checksum = checksum; + self + } +} + +const MAX_HEADER_LEN: usize = 0; + +const MAX_FOOTER_LEN: usize = MAX_CHECKSUM_LEN + 1; + +const MAX_FRAMING_LEN: usize = MAX_HEADER_LEN + MAX_FOOTER_LEN; + +impl Codec { + fn checksum(&self) -> &Checksum { + &self.config.checksum + } + + fn header_len(&self) -> usize { + 0 + } + + fn footer_len(&self) -> usize { + 1 + self.checksum().len() + } + + fn framing_len(&self) -> usize { + self.header_len() + self.footer_len() + } + + /// Encode the supplied payload data as a frame at the beginning of + /// the supplied buffer `dest`. Available from `no_std` crates. + /// + /// 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(&mut self, p: &Payload, dest: &mut Encoded) + -> Result { + // Panic if encoded frame won't fit in `dest` because this is a + // programmer error. + assert!(max_encoded_len(p.len()) <= dest.len()); #[cfg(feature = "trace")] { - println!("framed::encode: Header = {:?}", _header); + println!("framed::encode: Payload = {:?}", p); } - } - { - 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 cobs_len = cobs::encode(&p, &mut dest[self.header_len()..]); + let checksum = self.checksum(); + let checksum_value = checksum.calculate(p); + let checksum_len = checksum.len(); + + { + let mut _header = &mut dest[0..self.header_len()]; + + #[cfg(feature = "trace")] { + println!("framed::encode: Header = {:?}", _header); + } + } + { + let footer = &mut dest[ + (self.header_len() + cobs_len) + .. + (self.header_len() + cobs_len + self.footer_len())]; + footer[0..checksum_len].copy_from_slice(&*checksum_value); + footer[checksum_len] = FRAME_END_SYMBOL; + #[cfg(feature = "trace")] { + println!("framed::encode: Footer = {:?}", footer); + } + } + let len = self.header_len() + cobs_len + self.footer_len(); #[cfg(feature = "trace")] { - println!("framed::encode: Footer = {:?}", footer); + println!("framed::encode: Frame = {:?}", &dest[0..len]); } - } - let len = HEADER_LEN + cobs_len + FOOTER_LEN; - #[cfg(feature = "trace")] { - println!("framed::encode: Frame = {:?}", &dest[0..len]); - } - Ok(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 must 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`. Available from `no_std` crates. -/// -/// 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() >= e.len()`. -pub fn decode_to_slice(e: &Encoded, dest: &mut [u8]) --> Result { - #[cfg(feature = "trace")] { - println!("framed::decode: Encoded = {:?}", e); + Ok(len) } - if e.len() == 0 { - return Err(Error::EofBeforeFrame); + /// Encode the supplied payload data as a frame and return it on the heap. + #[cfg(feature = "use_std")] + pub fn encode_to_box(&mut self, p: &Payload) -> Result { + let mut buf = vec![0; max_encoded_len(p.len())]; + let len = self.encode_to_slice(p, &mut *buf)?; + buf.truncate(len); + Ok(BoxEncoded::from(buf)) } - if e.len() < FRAMING_LEN { - return Err(Error::EofDuringFrame); + /// 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 must do + /// so if this is required. + /// + /// Returns the length of the frame it has written. + #[cfg(feature = "use_std")] + pub fn encode_to_writer(&mut self, p: &Payload, w: &mut W) + -> Result { + let b = self.encode_to_box(p)?; + w.write_all(&*b.0)?; + Ok(b.len()) } - 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); - - 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::decode: header = {:?}\n\ - framed::decode: body = {:?}\n\ - framed::decode: footer = {:?}", - _header, body, footer); - } - - let decoded_len = cobs::decode(body, dest) - .map_err(|_| Error::CobsDecodeFailed)?; - - let decoded = &dest[0..decoded_len]; - - #[cfg(feature = "trace")] { - println!("framed::decode: payload = {:?}", - decoded); - } - - let calc_checksum = checksum(decoded); - let received_checksum = &footer[0..CHECKSUM_LEN]; - - #[cfg(feature = "trace")] { - println!("framed::decode: calc checksum = {:?}\n\ - framed::decode: recv checksum = {:?}", - calc_checksum, received_checksum); - } - - if calc_checksum != received_checksum { - return Err(Error::ChecksumError); - } - - Ok(decoded_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)); + /// Decode the supplied encoded frame, placing the payload at the + /// beginning of the supplied buffer `dest`. Available from `no_std` crates. + /// + /// 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() >= e.len()`. + pub fn decode_to_slice(&mut self, e: &Encoded, dest: &mut [u8]) + -> Result { #[cfg(feature = "trace")] { - println!("framed: Read result = {:?}", res); + println!("framed::decode: Encoded = {:?}", e); } - 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!(), - }; + if e.len() == 0 { + return Err(Error::EofBeforeFrame); + } + + if e.len() < self.framing_len() { + return Err(Error::EofDuringFrame); + } + + 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); + + let _header = &e[0..self.header_len()]; + let body = &e[self.header_len()..(e.len() - self.footer_len())]; + let footer = &e[(e.len() - self.footer_len())..e.len()]; #[cfg(feature = "trace")] { - println!("framed: Read byte = {}", b); + println!("framed::decode: header = {:?}\n\ + framed::decode: body = {:?}\n\ + framed::decode: footer = {:?}", + _header, body, footer); } - 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) -} + let decoded_len = cobs::decode(body, dest) + .map_err(|_| Error::CobsDecodeFailed)?; + + let decoded = &dest[0..decoded_len]; + + #[cfg(feature = "trace")] { + println!("framed::decode: payload = {:?}", + decoded); + } + + let checksum = self.checksum(); + let calc_checksum = checksum.calculate(decoded); + let received_checksum = &footer[0..checksum.len()]; + + #[cfg(feature = "trace")] { + println!("framed::decode: calc checksum = {:?}\n\ + framed::decode: recv checksum = {:?}", + calc_checksum, received_checksum); + } + + if &*calc_checksum != received_checksum { + return Err(Error::ChecksumError); + } + + Ok(decoded_len) + } + + /// Decode the supplied encoded frame, returning the payload on the heap. + #[cfg(feature = "use_std")] + pub fn decode_to_box(&mut self, e: &Encoded) -> Result { + if e.len() == 0 { + return Err(Error::EofBeforeFrame); + } + let mut buf = vec![0; max_decoded_len(e.len())]; + let len = self.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(&mut self, 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 self.decode_to_box(&*next_frame), + Ok(0) => + return self.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); + + self.decode_to_box(&*next_frame) + } +} // End of impl Codec. const_fn! { /// Returns an upper bound for the decoded length of the payload @@ -308,7 +395,7 @@ const_fn! { /// Useful for calculating an appropriate buffer length. pub fn max_decoded_len(code_len: usize) -> usize { // This is an over-estimate of the required decoded buffer, but - // wasting HEADER_LEN + FOOTER_LEN bytes should be acceptable and + // wasting MAX_HEADER_LEN + MAX_FOOTER_LEN bytes should be acceptable and // we can calculate this trivially in a const fn. code_len } @@ -320,9 +407,8 @@ const_fn! { /// /// Useful for calculating an appropriate buffer length. pub fn max_encoded_len(payload_len: usize) -> usize { - HEADER_LEN + MAX_FRAMING_LEN + cobs_max_encoded_len(payload_len) - + FOOTER_LEN } } @@ -348,20 +434,16 @@ const_fn! { } /// Sends encoded frames over an inner `std::io::Write` instance. +/// +/// Construct an instance using the method `Config::to_sender` #[cfg(feature = "use_std")] pub struct Sender { + codec: Codec, w: W, } #[cfg(feature = "use_std")] impl Sender { - /// Construct a `Sender` that writes encoded frames to `w`. - pub fn new(w: W) -> Sender { - Sender:: { - w: w, - } - } - /// Consume this `Sender` and return the inner `std::io::Write`. pub fn into_inner(self) -> W { self.w @@ -381,7 +463,7 @@ impl Sender { /// /// See also: [`send`](#method.send) pub fn queue(&mut self, p: &Payload) -> Result { - encode_to_writer(p, &mut self.w) + self.codec.encode_to_writer(p, &mut self.w) } /// Encode the supplied payload as a frame, write it to the @@ -399,21 +481,16 @@ impl Sender { } /// Receives encoded frames from an inner `std::io::Read` instance. +/// +/// Construct an instance using the method `Config::to_receiver` #[cfg(feature = "use_std")] pub struct Receiver { + codec: Codec, r: R, } #[cfg(feature = "use_std")] impl Receiver { - /// Construct a `Receiver` that receives frames from the supplied - /// `std::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 @@ -422,7 +499,7 @@ impl Receiver { /// 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) + self.codec.decode_from_reader::(&mut self.r) } } @@ -450,6 +527,10 @@ mod tests { assert_eq!(max_decoded_len(255), 255); } + fn codec() -> Codec { + Config::default().into_codec() + } + // A test payload. const PAYLOAD: [u8; PAYLOAD_LEN] = [0, 1, 2, 3]; const PAYLOAD_LEN: usize = 4; @@ -458,7 +539,7 @@ mod tests { /// Returns an encoded frame with payload PAYLOAD. fn encoded_payload(buf: &mut [u8; ENCODED_LEN]) -> &[u8] { - let len = encode_to_slice(&PAYLOAD, buf).unwrap(); + let len = codec().encode_to_slice(&PAYLOAD, buf).unwrap(); &buf[0..len] } @@ -470,7 +551,7 @@ mod tests { encoded, payload); } let mut decoded_buf = [0; 100]; - let len = decode_to_slice(encoded, &mut decoded_buf).unwrap(); + let len = codec().decode_to_slice(encoded, &mut decoded_buf).unwrap(); let decoded = &decoded_buf[0..len]; assert_eq!(decoded, payload); } @@ -479,14 +560,14 @@ mod tests { #[should_panic] fn encode_to_slice_dest_too_small() { let mut encoded_buf = [0u8; PAYLOAD_LEN];; - let _ = encode_to_slice(&PAYLOAD, &mut encoded_buf); + let _ = codec().encode_to_slice(&PAYLOAD, &mut encoded_buf); } #[test] #[cfg(feature = "use_std")] fn encode_to_slice_ok_dynamic_dest() { let mut encoded_buf = vec![0u8; max_encoded_len(PAYLOAD.len())]; - let len = encode_to_slice(&PAYLOAD, &mut *encoded_buf).unwrap(); + let len = codec().encode_to_slice(&PAYLOAD, &mut *encoded_buf).unwrap(); let encoded = &encoded_buf[0..len]; assert_payload_eq(encoded, &PAYLOAD); @@ -497,7 +578,7 @@ mod tests { #[cfg(feature = "use_nightly")] fn encode_to_slice_ok_static_dest() { let mut encoded_buf = [0u8; max_encoded_len(PAYLOAD_LEN)]; - let len = encode_to_slice(&PAYLOAD, &mut encoded_buf).unwrap(); + let len = codec().encode_to_slice(&PAYLOAD, &mut encoded_buf).unwrap(); let encoded = &encoded_buf[0..len]; assert_payload_eq(encoded, &PAYLOAD); @@ -507,7 +588,7 @@ mod tests { #[cfg(feature = "use_std")] fn encode_to_writer_ok() { let mut encoded = vec![]; - encode_to_writer(&PAYLOAD, &mut encoded).unwrap(); + codec().encode_to_writer(&PAYLOAD, &mut encoded).unwrap(); assert_payload_eq(&*encoded, &PAYLOAD); } @@ -517,15 +598,15 @@ mod tests { let mut buf = [0; ENCODED_LEN]; let encoded = encoded_payload(&mut buf); let mut decoded_buf = [0u8; PAYLOAD_LEN - 1]; - let _ = decode_to_slice(&*encoded, &mut decoded_buf); + let _ = codec().decode_to_slice(&*encoded, &mut decoded_buf); } #[test] #[cfg(feature = "use_std")] fn decode_to_slice_ok_dynamic_dest() { - let encoded = encode_to_box(&PAYLOAD).unwrap(); + let encoded = codec().encode_to_box(&PAYLOAD).unwrap(); let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())]; - let len = decode_to_slice(&*encoded, &mut decoded_buf).unwrap(); + let len = codec().decode_to_slice(&*encoded, &mut decoded_buf).unwrap(); let decoded = &decoded_buf[0..len]; assert_eq!(&PAYLOAD, decoded); @@ -536,7 +617,7 @@ mod tests { fn decode_to_slice_no_end_symbol() { let encoded = vec![FRAME_END_SYMBOL + 1; max_encoded_len(0)]; let mut decoded_buf = []; - let res = decode_to_slice(&*encoded, &mut decoded_buf); + let res = codec().decode_to_slice(&*encoded, &mut decoded_buf); match res { Err(Error::EofDuringFrame) => (), @@ -547,9 +628,10 @@ mod tests { #[test] #[cfg(feature = "use_std")] fn decode_to_slice_encoded_too_short() { - let encoded = vec![FRAME_END_SYMBOL; FRAMING_LEN - 1]; + let mut c = codec(); + let encoded = vec![FRAME_END_SYMBOL; c.framing_len() - 1]; let mut decoded_buf = []; - let res = decode_to_slice(&*encoded, &mut decoded_buf); + let res = c.decode_to_slice(&*encoded, &mut decoded_buf); match res { Err(Error::EofDuringFrame) => (), @@ -562,7 +644,7 @@ mod tests { fn decode_to_slice_encoded_empty() { let encoded = vec![]; let mut decoded_buf = []; - let res = decode_to_slice(&*encoded, &mut decoded_buf); + let res = codec().decode_to_slice(&*encoded, &mut decoded_buf); match res { Err(Error::EofBeforeFrame) => (), @@ -573,19 +655,21 @@ mod tests { #[test] #[cfg(feature = "use_std")] fn decode_to_slice_bad_checksum() { - let encoded = encode_to_box(&PAYLOAD).unwrap(); + let mut c = codec(); + let encoded = c.encode_to_box(&PAYLOAD).unwrap(); let mut encoded = Vec::from(&*encoded); - let checksum_offset = encoded.len() - FOOTER_LEN; + let checksum_offset = encoded.len() - c.footer_len(); { - let checksum = &mut encoded[checksum_offset.. - (checksum_offset + CHECKSUM_LEN)]; + let checksum = + &mut encoded[checksum_offset.. + (checksum_offset + c.config.checksum.len())]; checksum[0] = checksum[0].wrapping_add(1); checksum[1] = checksum[1].wrapping_add(2); } let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())]; - let res = decode_to_slice(&*encoded, &mut decoded_buf); + let res = codec().decode_to_slice(&*encoded, &mut decoded_buf); match res { Err(Error::ChecksumError) => (), @@ -596,11 +680,11 @@ mod tests { #[test] #[cfg(feature = "use_std")] fn decode_to_slice_missing_bytes() { - let encoded = encode_to_box(&PAYLOAD).unwrap(); + let encoded = codec().encode_to_box(&PAYLOAD).unwrap(); let encoded = &encoded[1..encoded.len()]; let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())]; - let res = decode_to_slice(&*encoded, &mut decoded_buf); + let res = codec().decode_to_slice(&*encoded, &mut decoded_buf); match res { Err(Error::ChecksumError) => (), @@ -611,8 +695,8 @@ mod tests { #[test] #[cfg(feature = "use_std")] fn decode_to_box_ok() { - let encoded = encode_to_box(&PAYLOAD).unwrap(); - let decoded = decode_to_box(&*encoded).unwrap(); + let encoded = codec().encode_to_box(&PAYLOAD).unwrap(); + let decoded = codec().decode_to_box(&*encoded).unwrap(); assert_eq!(&PAYLOAD, &*decoded); } @@ -620,9 +704,10 @@ mod tests { #[test] #[cfg(feature = "use_std")] fn decode_from_reader_ok() { - let encoded = encode_to_box(&PAYLOAD).unwrap(); + let mut c = codec(); + let encoded = c.encode_to_box(&PAYLOAD).unwrap(); let mut reader = Cursor::new(&*encoded); - let decoded = decode_from_reader::>(&mut reader).unwrap(); + let decoded = c.decode_from_reader::>(&mut reader).unwrap(); assert_eq!(&*decoded, &PAYLOAD); } } @@ -692,9 +777,9 @@ mod rw_tests { #[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(); + let chan = Channel::new(); + let mut rx = config().into_receiver(chan.reader()); + let mut tx_raw = chan.writer(); tx_raw.write(&[0x01]).unwrap(); match rx.recv() { Err(Error::EofDuringFrame) => (), @@ -702,10 +787,15 @@ mod rw_tests { } } + fn config() -> Config { + Config::default() + } + 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); + let chan = Channel::new(); + let c = config(); + let tx = c.clone().into_sender(Box::new(chan.writer()) as Box); + let rx = c.clone().into_receiver(Box::new(chan.reader()) as Box); (tx, rx) } } diff --git a/framed/src/channel.rs b/framed/src/channel.rs index e798f28..1876dc6 100644 --- a/framed/src/channel.rs +++ b/framed/src/channel.rs @@ -1,6 +1,4 @@ //! Vec-backed FIFO buffer of bytes for testing. -//! -//! TODO: Remove this, I have an easy workaround. use std::cell::RefCell; use std::io::{Read, Result, Write}; diff --git a/framed/src/checksum.rs b/framed/src/checksum.rs new file mode 100644 index 0000000..dd2f620 --- /dev/null +++ b/framed/src/checksum.rs @@ -0,0 +1,78 @@ +use ::Payload; +use byteorder::{self, ByteOrder}; +use core::cmp::PartialEq; +use core::fmt::{self, Debug, Formatter}; +use core::ops::Deref; +use crc16; + +/// A checksum algorithm configuration to use when encoding data. +#[derive(Clone, Debug)] +pub enum Checksum { + /// Use no checksum. + None, + + /// CRC-16/CDMA2000 as [implemented in crate `crc16`][impl]. + /// This is the default checksum. + /// + /// [impl]: https://docs.rs/crc16/0.3.4/crc16/enum.CDMA2000.html + Crc16Cdma2000, +} + +impl Default for Checksum { + fn default() -> Checksum { + Checksum::Crc16Cdma2000 + } +} + +pub(crate) const MAX_CHECKSUM_LEN: usize = 2; + +#[derive(Eq)] +pub(crate) struct ChecksumValue { + data: [u8; MAX_CHECKSUM_LEN], + len: usize, +} + +impl Checksum { + pub(crate) fn len(&self) -> usize { + match *self { + Checksum::None => 0, + Checksum::Crc16Cdma2000 => 2, + } + } + + pub(crate) fn calculate(&self, payload: &Payload) -> ChecksumValue { + let mut v = ChecksumValue { + data: [0u8; MAX_CHECKSUM_LEN], + len: self.len(), + }; + + match *self { + Checksum::None => (), + Checksum::Crc16Cdma2000 => { + let u16 = crc16::State::::calculate(payload); + byteorder::NetworkEndian::write_u16(&mut v.data, u16); + }, + }; + + v + } +} + +impl Deref for ChecksumValue { + type Target = [u8]; + fn deref(&self) -> &[u8] { + &self.data[0..self.len] + } +} + +impl PartialEq for ChecksumValue { + fn eq(&self, other: &ChecksumValue) -> bool { + self.deref() == other.deref() + } +} + +impl Debug for ChecksumValue { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + write!(f, "{:?}", self.deref()) + } +} diff --git a/framed/src/lib.rs b/framed/src/lib.rs index 2f79a24..aa2165a 100644 --- a/framed/src/lib.rs +++ b/framed/src/lib.rs @@ -26,7 +26,7 @@ //! * Nothing here yet. //! * Body: payload [COBS]-encoded to remove bytes equal to zero //! * Footer: -//! * A 2 byte checksum. +//! * A variable length checksum, depending on configuration in `framed::bytes::Config`. //! * A terminating zero byte. //! //! [COBS]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing @@ -83,6 +83,7 @@ //! the `use_std` Cargo feature. #![deny(warnings)] +#![deny(missing_docs)] #![cfg_attr(not(feature = "use_std"), no_std)] #![cfg_attr(feature = "use_nightly", feature(const_fn))] @@ -196,8 +197,10 @@ extern crate ssmarshal; // ## Sub-modules pub mod bytes; -#[cfg(feature = "use_std")] -pub mod channel; +#[cfg(all(test, feature = "use_std"))] +mod channel; + +mod checksum; pub mod error; pub use error::{Error, Result}; diff --git a/framed/src/typed.rs b/framed/src/typed.rs index 8a3d3f7..99ef853 100644 --- a/framed/src/typed.rs +++ b/framed/src/typed.rs @@ -34,16 +34,17 @@ //! } //! //! let payload = Test { a: 1, b: 2 }; +//! let config = framed::bytes::Config::default().typed::(); //! //! let mut encoded = vec![]; //! { -//! let mut sender = Sender::<_, Test>::new(&mut encoded); +//! let mut sender = config.clone().into_sender(&mut encoded); //! sender.send(&payload).expect("send ok"); //! } //! //! // `encoded` now contains the encoded value. //! -//! let mut receiver = Receiver::<_, Test>::new(Cursor::new(encoded)); +//! let mut receiver = config.clone().into_receiver(Cursor::new(encoded)); //! let decoded = receiver.recv().expect("recv ok"); //! //! assert_eq!(payload, decoded); @@ -58,6 +59,7 @@ //! //! ```rust //! # extern crate framed; +//! # use framed::bytes; //! # use framed::typed::*; //! # extern crate serde; //! # #[macro_use] @@ -71,10 +73,12 @@ //! # } //! # //! # let payload = Test { a: 1, b: 2 }; +//! # let config = bytes::Config::default().typed::(); //! # +//! let mut codec = config.into_codec(); //! let mut ser_buf = [0u8; max_serialize_buf_len::()]; //! let mut encoded_buf = [0u8; max_encoded_len::()]; -//! let encoded_len = encode_to_slice::( +//! let encoded_len = codec.encode_to_slice( //! &payload, //! &mut ser_buf, //! &mut encoded_buf @@ -84,8 +88,8 @@ //! // `encoded` now contains the complete encoded frame. //! //! let mut de_buf = [0u8; max_serialize_buf_len::()]; -//! let decoded = decode_from_slice(encoded, &mut de_buf) -//! .expect("decode ok"); +//! let decoded = codec.decode_from_slice(encoded, &mut de_buf) +//! .expect("decode ok"); //! //! assert_eq!(payload, decoded); //! # } @@ -94,7 +98,6 @@ use ::{Encoded, TempBuffer}; use ::bytes; use ::error::{Result}; -#[cfg(feature = "use_std")] use core::marker::PhantomData; use core::mem::size_of; use serde::Serialize; @@ -103,64 +106,131 @@ use ssmarshal; #[cfg(feature = "use_std")] use std::io::{Read, Write}; -/// Serializes and encodes the supplied value `v` into destination -/// buffer `dest`, using `ser_buf` as a temporary serialization buffer. -/// Available from `no_std` crates. +/// Contains methods for encoding and decoding a serializable type `T` +/// with a specific configuration. /// -/// Returns the number of bytes written to the beginning of `dest`. -/// -/// ## Panics -/// -/// This will panic if the supplied buffers are too small to serialize -/// a value of `T`. Callers must ensure that: -/// -/// * `ser_buf.len() >= max_serialize_buf_len::()` and -/// * `dest.len() >= max_encoded_len::()`. -/// -/// ## Examples -/// -/// See the [no_std usage example][ex] in the `typed` module documentation. -/// [ex]: index.html#example-usage-from-a-no_std-crate -pub fn encode_to_slice( - v: &T, - ser_buf: &mut TempBuffer, - dest: &mut Encoded, -) -> Result { - assert!(ser_buf.len() >= max_serialize_buf_len::()); - assert!(dest.len() >= max_encoded_len::()); - - let ser_len = ssmarshal::serialize(ser_buf, v)?; - let ser = &ser_buf[0..ser_len]; - bytes::encode_to_slice(ser, dest) +/// Construct an instance from a `Config` instance with the +/// `Config::into_codec` method. +pub struct Codec { + bytes_codec: bytes::Codec, + _phantom: PhantomData, } -/// Decodes the supplied encoded frame `e`, then deserializes its -/// payload as a value of type `T`, using `de_buf` as a temporary -/// deserialization buffer. Available from `no_std` crates. +/// Configurable options for encoding and decoding a serializable type +/// `T` using a builder pattern. /// -/// Returns the deserialized value. -/// -/// ## Panics -/// -/// This will panic if the supplied buffer is too small to deserialize -/// a value of `T`. Callers must ensure that: -/// -/// * `de_buf.len() >= max_serialize_buf_len::()`. -/// -/// ## Examples -/// -/// See the [no_std usage example][ex] in the `typed` module documentation. -/// [ex]: index.html#example-usage-from-a-no_std-crate -pub fn decode_from_slice( - e: &Encoded, - de_buf: &mut TempBuffer -) -> Result { - assert!(de_buf.len() >= max_serialize_buf_len::()); +/// Construct an instance with the method `framed::bytes::Config::typed()`. +pub struct Config { + bytes_config: bytes::Config, + _phantom: PhantomData, +} - let de_len = bytes::decode_to_slice(e, de_buf)?; - let payload = &de_buf[0..de_len]; - let (v, _len) = ssmarshal::deserialize(payload)?; - Ok(v) +impl Clone for Config { + fn clone(&self) -> Config { + Config:: { + bytes_config: self.bytes_config.clone(), + _phantom: PhantomData::::default(), + } + } +} + +impl Config { + pub(crate) fn new(bytes_config: bytes::Config) -> Config { + Config:: { + bytes_config: bytes_config, + _phantom: PhantomData::::default(), + } + } + + /// Construct a `Codec` instance with this configuration. + pub fn into_codec(self) -> Codec { + Codec:: { + bytes_codec: self.bytes_config.into_codec(), + _phantom: PhantomData::::default(), + } + } + + #[cfg(feature = "use_std")] + /// Construct a `Receiver` instance with this configuration. + pub fn into_receiver(self, r: R) -> Receiver { + Receiver:: { + codec: self.into_codec(), + r: r, + } + } + + #[cfg(feature = "use_std")] + /// Construct a `Sender` instance with this configuration. + pub fn into_sender(self, w: W) -> Sender { + Sender:: { + codec: self.into_codec(), + w: w, + } + } +} + +impl Codec { + /// Serializes and encodes the supplied value `v` into destination + /// buffer `dest`, using `ser_buf` as a temporary serialization buffer. + /// Available from `no_std` crates. + /// + /// Returns the number of bytes written to the beginning of `dest`. + /// + /// ## Panics + /// + /// This will panic if the supplied buffers are too small to serialize + /// a value of `T`. Callers must ensure that: + /// + /// * `ser_buf.len() >= max_serialize_buf_len::()` and + /// * `dest.len() >= max_encoded_len::()`. + /// + /// ## Examples + /// + /// See the [no_std usage example][ex] in the `typed` module documentation. + /// [ex]: index.html#example-usage-from-a-no_std-crate + pub fn encode_to_slice( + &mut self, + v: &T, + ser_buf: &mut TempBuffer, + dest: &mut Encoded, + ) -> Result { + assert!(ser_buf.len() >= max_serialize_buf_len::()); + assert!(dest.len() >= max_encoded_len::()); + + let ser_len = ssmarshal::serialize(ser_buf, v)?; + let ser = &ser_buf[0..ser_len]; + self.bytes_codec.encode_to_slice(ser, dest) + } + + /// Decodes the supplied encoded frame `e`, then deserializes its + /// payload as a value of type `T`, using `de_buf` as a temporary + /// deserialization buffer. Available from `no_std` crates. + /// + /// Returns the deserialized value. + /// + /// ## Panics + /// + /// This will panic if the supplied buffer is too small to deserialize + /// a value of `T`. Callers must ensure that: + /// + /// * `de_buf.len() >= max_serialize_buf_len::()`. + /// + /// ## Examples + /// + /// See the [no_std usage example][ex] in the `typed` module documentation. + /// [ex]: index.html#example-usage-from-a-no_std-crate + pub fn decode_from_slice( + &mut self, + e: &Encoded, + de_buf: &mut TempBuffer + ) -> Result { + assert!(de_buf.len() >= max_serialize_buf_len::()); + + let de_len = self.bytes_codec.decode_to_slice(e, de_buf)?; + let payload = &de_buf[0..de_len]; + let (v, _len) = ssmarshal::deserialize(payload)?; + Ok(v) + } } const_fn! { @@ -185,27 +255,20 @@ const_fn! { /// Sends encoded structs of type `T` over an inner `std::io::Write` instance. /// +/// Construct an instance using the method `Config::to_sender` +/// /// ## Examples /// /// See the [std usage example][ex] in the `typed` module documentation. /// [ex]: index.html#example-usage-from-a-std-crate #[cfg(feature = "use_std")] -pub struct Sender { +pub struct Sender { + codec: Codec, w: W, - _t: PhantomData, } #[cfg(feature = "use_std")] -impl Sender { - /// Construct a `Sender` that sends encoded structs over the supplied - /// `io::Write`. - pub fn new(w: W) -> Sender { - Sender:: { - w: w, - _t: PhantomData::default(), - } - } - +impl Sender { /// Consume this `Sender` and return the inner `std::io::Write`. pub fn into_inner(self) -> W { self.w @@ -239,11 +302,11 @@ impl Sender { // I think this may require const generics // (rust-lang tracking issue: // https://github.com/rust-lang/rust/issues/44580). - let mut ser_buf = vec![0u8; size_of::()]; + let mut ser_buf = vec![0u8; max_serialize_buf_len::()]; let ser_len = ssmarshal::serialize(&mut ser_buf, v)?; let ser = &ser_buf[0..ser_len]; - bytes::encode_to_writer(&ser, &mut self.w) + self.codec.bytes_codec.encode_to_writer(&ser, &mut self.w) } /// Encode the supplied payload as a frame, write it to the @@ -262,27 +325,20 @@ impl Sender { /// Receives encoded structs of type `T` from an inner `std::io::Read` instance. /// +/// Construct an instance using the method `Config::to_receiver` +/// /// ## Examples /// /// See the [std usage example][ex] in the `typed` module documentation. /// [ex]: index.html#example-usage-from-a-std-crate #[cfg(feature = "use_std")] -pub struct Receiver { +pub struct Receiver { + codec: Codec, r: R, - _t: PhantomData, } #[cfg(feature = "use_std")] -impl Receiver { - /// Construct a `Receiver` that receives encoded structs from the supplied - /// `io::Read`. - pub fn new(r: R) -> Receiver { - Receiver:: { - r: r, - _t: PhantomData::default(), - } - } - +impl Receiver { /// Consume this `Receiver` and return the inner `std::io::Read`. pub fn into_inner(self) -> R { self.r @@ -291,7 +347,8 @@ impl Receiver { /// Receive an encoded frame from the inner `std::io::Read`, decode it /// and return the payload. pub fn recv(&mut self) -> Result { - let payload = bytes::decode_from_reader::(&mut self.r)?; + let payload = + self.codec.bytes_codec.decode_from_reader::(&mut self.r)?; let (v, _len) = ssmarshal::deserialize(&*payload)?; Ok(v) } @@ -342,6 +399,10 @@ mod tests { v } + fn codec() -> Codec { + bytes::Config::default().typed::().into_codec() + } + mod slice_tests { use super::*; @@ -349,14 +410,15 @@ mod tests { #[cfg(feature = "use_nightly")] fn roundtrip() { let input = test_val(); + let mut c = codec(); let mut ser_buf = [0u8; max_serialize_buf_len::()]; let mut enc_buf = [0u8; max_encoded_len::()]; - let len = super::encode_to_slice( + let len = c.encode_to_slice( &input, &mut ser_buf, &mut enc_buf ).unwrap(); let enc = &enc_buf[0..len]; - let output = super::decode_from_slice(&enc, &mut ser_buf).unwrap(); + let output = c.decode_from_slice(&enc, &mut ser_buf).unwrap(); assert_eq!(input, output); } @@ -365,7 +427,7 @@ mod tests { fn bad_ser_buf_len() { let mut ser_buf = [0u8; 1]; let mut enc = [0u8; 100]; - super::encode_to_slice( + codec().encode_to_slice( &test_val(), &mut ser_buf, &mut enc ).unwrap(); } @@ -375,7 +437,7 @@ mod tests { fn bad_dest_len() { let mut ser_buf = [0u8; 100]; let mut enc = [0u8; 1]; - super::encode_to_slice( + codec().encode_to_slice( &test_val(), &mut ser_buf, &mut enc ).unwrap(); } @@ -427,9 +489,12 @@ mod tests { } fn pair() -> (Sender, Test>, Receiver, Test>) { - let c = Channel::new(); - let tx = Sender::new(Box::new(c.writer()) as Box); - let rx = Receiver::new(Box::new(c.reader()) as Box); + let chan = Channel::new(); + let conf = bytes::Config::default().typed::(); + let tx = conf.clone() + .into_sender(Box::new(chan.writer()) as Box); + let rx = conf.clone() + .into_receiver(Box::new(chan.reader()) as Box); (tx, rx) } } diff --git a/test_type/src/main.rs b/test_type/src/main.rs index 40855a5..8670a31 100644 --- a/test_type/src/main.rs +++ b/test_type/src/main.rs @@ -3,7 +3,6 @@ extern crate framed; extern crate framed_test_type as lib; -use framed::typed::Sender; use lib::Test; use std::io::stdout; @@ -14,6 +13,8 @@ fn main() { }; eprintln!("test_type/main.rs: Sending sample value: {:#?}", t); - let mut s = Sender::<_, Test>::new(stdout()); + let mut s = framed::bytes::Config::default() + .typed::() + .into_sender(stdout()); s.send(&t).unwrap(); }