From 98582cc9aecc215950af5942a66b66610ebf4327 Mon Sep 17 00:00:00 2001 From: Alex Helfet Date: Thu, 28 Dec 2017 21:00:44 +0000 Subject: [PATCH] Docs, usage examples, made max_decoded_len a const fn for sizing buffers. --- framed/Cargo.toml | 2 +- framed/src/channel.rs | 2 + framed/src/lib.rs | 138 ++++++++++++++++++++++++++---------------- framed/src/typed.rs | 64 ++++++++++---------- 4 files changed, 121 insertions(+), 85 deletions(-) diff --git a/framed/Cargo.toml b/framed/Cargo.toml index 6f60201..fb257b4 100644 --- a/framed/Cargo.toml +++ b/framed/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framed" -# Next version is 0.2.0. I changed the type of max_encoded_len +# Next version is 0.2.0. I changed the type of max_encoded_len, max_decoded_len. # was: pub fn max_encoded_len(usize) -> Result # now: pub const fn max_encoded_len(usize) -> usize version = "0.1.4" diff --git a/framed/src/channel.rs b/framed/src/channel.rs index 1876dc6..e798f28 100644 --- a/framed/src/channel.rs +++ b/framed/src/channel.rs @@ -1,4 +1,6 @@ //! 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/lib.rs b/framed/src/lib.rs index 12394c6..710039c 100644 --- a/framed/src/lib.rs +++ b/framed/src/lib.rs @@ -34,16 +34,17 @@ //! //! `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 +//! `trace`: Enable to print all data to stdout for testing. //! -//! 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: +//! ## Byte slice wrapper types +//! +//! Payload data and encoded frame data are both slices of bytes but +//! have separate types to help 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. @@ -66,12 +67,6 @@ //! 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`) @@ -79,17 +74,67 @@ //! 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. +//! See the `decode_*` and `encode_*` functions for simple uses with +//! various input and output types. +//! +//! The `Sender` struct writes encoded payloads to an +//! inner `std::io::Write` instance, and the `Receiver` struct reads +//! and decodes payloads from an inner `std::io::Read` instance. +//! +//! ```rust +//! # use framed::*; +//! # use std::io::Cursor; +//! # +//! let payload = [1, 2, 3]; +//! +//! let mut encoded = vec![]; +//! { +//! let mut sender = Sender::new(&mut encoded); +//! sender.send(&payload).expect("send ok"); +//! } +//! +//! // `encoded` now contains the encoded frame. +//! +//! let mut receiver = Receiver::new(Cursor::new(encoded)); +//! let decoded = receiver.recv().expect("recv ok"); +//! +//! assert_eq!(payload, *decoded); +//! ``` //! //! ## Example usage from a no_std crate //! -//! TODO. +//! The `encode_to_slice` and `decode_from_slice` functions offer an +//! API for `no_std` crates that might not have a heap allocator +//! available and cannot use `std::io::Read` or `std::io::Write`. +//! +//! ```rust +//! # use framed::*; +//! # +//! // 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; +//! +//! // The maximum payload length implies a maximum encoded frame length, +//! // which we can use for frame buffers. +//! const MAX_FRAME_LEN: usize = max_encoded_len(MAX_PAYLOAD_LEN); +//! +//! let payload: [u8; 3] = [1, 2, 3]; +//! 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 = &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 = &decoded_buf[0..decoded_len]; +//! +//! assert_eq!(payload, *decoded); +//! ``` //! #![deny(warnings)] @@ -243,7 +288,7 @@ pub fn encode_to_box(p: &Payload) -> Result { /// 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 +/// 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. @@ -278,8 +323,8 @@ pub fn encode_to_writer(p: &Payload, w: &mut W) -> Result { /// # 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]) +/// Ensure `dest.len() >= e.len()`. +pub fn decode_to_slice(e: &Encoded, dest: &mut [u8]) -> Result { #[cfg(feature = "trace")] { println!("framed: Encoded input = {:?}", e); @@ -293,19 +338,19 @@ pub fn decode_to_slice(e: &Encoded, mut dest: &mut [u8]) return Err(Error::EofDuringFrame) } - assert!(dest.len() >= max_decoded_len(e.len())?); + 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) + let len = cobs::decode(body, dest) .map_err(|_| Error::CobsDecodeFailed)?; #[cfg(feature = "trace")] { println!("framed: body = {:?}\n\ framed: decoded = {:?}", - body, &dest[0..len]); + body, dest[0..len]); } Ok(len) @@ -317,7 +362,7 @@ 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 mut buf = vec![0; max_decoded_len(e.len())]; let len = decode_to_slice(e, &mut buf)?; buf.truncate(len); Ok(BoxPayload::from(buf)) @@ -365,16 +410,11 @@ pub fn decode_from_reader(r: &mut Read) -> Result { /// 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) +pub const 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 + // we can calculate this trivially in a const fn. + code_len } /// Returns an upper bound for the encoded length of a frame with @@ -422,7 +462,7 @@ const fn cobs_max_encoded_len(payload_len: usize) -> usize { + 1 } -/// Sends encoded frames over an inner `io::Write` instance. +/// Sends encoded frames over an inner `std::io::Write` instance. #[cfg(feature = "use_std")] pub struct Sender { w: W, @@ -430,15 +470,14 @@ pub struct Sender { #[cfg(feature = "use_std")] impl Sender { - /// Construct a `Sender` that sends frames over the supplied - /// `io::Write`. + /// 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 `io::Write`. + /// Consume this `Sender` and return the inner `std::io::Write`. pub fn into_inner(self) -> W { self.w } @@ -474,7 +513,7 @@ impl Sender { } } -/// Receives encoded frames from an inner `io::Read` instance. +/// Receives encoded frames from an inner `std::io::Read` instance. #[cfg(feature = "use_std")] pub struct Receiver { r: R, @@ -483,7 +522,7 @@ pub struct Receiver { #[cfg(feature = "use_std")] impl Receiver { /// Construct a `Receiver` that receives frames from the supplied - /// `io::Read`. + /// `std::io::Read`. pub fn new(r: R) -> Receiver { Receiver:: { r: r, @@ -515,20 +554,13 @@ mod tests { 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); + assert_eq!(max_decoded_len(0) , 0); + assert_eq!(max_decoded_len(1) , 1); + assert_eq!(max_decoded_len(2) , 2); + assert_eq!(max_decoded_len(3) , 3); + assert_eq!(max_decoded_len(255), 255); } } diff --git a/framed/src/typed.rs b/framed/src/typed.rs index b57b967..4217679 100644 --- a/framed/src/typed.rs +++ b/framed/src/typed.rs @@ -1,14 +1,16 @@ -//! Sending and receiving structs serialized with serde and -//! [`ssmarshal`][ssmarshal]. +//! Sending and receiving serde-serialized structs. +//! +//! Currently structs are serialized with [`ssmarshal`][ssmarshal], +//! but this is an implementation detail that may change without +//! warning. `ssmarshal` uses a straightforward, compact serialization +//! format that doesn't support compatibility between versions or +//! dynamic length types (arrays, maps). Its lack of versioning fits +//! with the design goals for the frame encoding in this crate: +//! unsuitable for long-term storage or transmission between different +//! versions of an application, but space-efficient. //! //! [ssmarshal]: https://crates.io/crates/ssmarshal //! -//! `ssmarshal` uses a straightforward, compact serialization format -//! that doesn't support compatibility between versions or dynamic -//! length types (arrays, maps). Its lack of stability fits with the -//! frame encoding in this crate: unsuitable for long-term storage or -//! transmission between different versions of an application. -//! //! ## Example usage from a `std` crate //! //! The `Sender` struct writes serialized and encoded values to an @@ -31,28 +33,28 @@ //! b: u16, //! } //! -//! let input = Test { a: 1, b: 2 }; +//! let payload = Test { a: 1, b: 2 }; //! -//! let mut data = vec![]; +//! let mut encoded = vec![]; //! { -//! let mut sender = Sender::<_, Test>::new(&mut data); -//! sender.send(&input).expect("send ok"); +//! let mut sender = Sender::<_, Test>::new(&mut encoded); +//! sender.send(&payload).expect("send ok"); //! } //! -//! // `data` now contains the encoded value. +//! // `encoded` now contains the encoded value. //! -//! let mut receiver = Receiver::<_, Test>::new(Cursor::new(data)); -//! let output = receiver.recv().expect("recv ok"); +//! let mut receiver = Receiver::<_, Test>::new(Cursor::new(encoded)); +//! let decoded = receiver.recv().expect("recv ok"); //! -//! assert_eq!(input, output); +//! assert_eq!(payload, decoded); //! # } //! ``` //! //! ## 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 -//! available and cannot use `std::io::Read` or `std::io::Write`. +//! API for `no_std` crates that do not have a heap allocator and +//! cannot use `std::io::Read` or `std::io::Write`. //! //! ```rust //! # extern crate framed; @@ -68,12 +70,12 @@ //! # b: u16, //! # } //! # -//! # let input = Test { a: 1, b: 2 }; +//! # let payload = Test { a: 1, b: 2 }; //! # //! let mut ser_buf = [0u8; max_serialize_buf_len::()]; //! let mut encoded_buf = [0u8; max_encoded_len::()]; //! let encoded_len = encode_to_slice::( -//! &input, +//! &payload, //! &mut ser_buf, //! &mut encoded_buf //! ).expect("encode ok"); @@ -82,10 +84,10 @@ //! // `encoded` now contains the complete encoded frame. //! //! let mut de_buf = [0u8; max_serialize_buf_len::()]; -//! let output = decode_from_slice(encoded, &mut de_buf) -//! .expect("decode ok"); +//! let decoded = decode_from_slice(encoded, &mut de_buf) +//! .expect("decode ok"); //! -//! assert_eq!(input, output); +//! assert_eq!(payload, decoded); //! # } //! ``` @@ -170,7 +172,7 @@ pub const fn max_encoded_len() -> usize { /// length needed by `encode_to_slice` and `decode_from_slice` when /// serializing or deserializing a value of type `T`. pub const fn max_serialize_buf_len() -> usize { - size_of::() + super::max_encoded_len(size_of::()) } @@ -374,17 +376,17 @@ mod tests { #[test] fn serialize_buf_len() { - assert_eq!(max_serialize_buf_len::(), 40); - assert_eq!(max_serialize_buf_len::(), 1); - assert_eq!(max_serialize_buf_len::(), 2); - assert_eq!(max_serialize_buf_len::(), 4); - assert_eq!(max_serialize_buf_len::(), 8); + 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); } #[test] fn encoded_len() { - assert_eq!(max_encoded_len::(), 42); - assert_eq!(max_encoded_len::(), 3); + assert_eq!(max_encoded_len::(), 44); + assert_eq!(max_encoded_len::(), 5); } }