1
Fork 0

Docs, usage examples, made max_decoded_len a const fn for sizing buffers.

This commit is contained in:
Alex Helfet 2017-12-28 21:00:44 +00:00
parent 4123ca0329
commit 98582cc9ae
4 changed files with 121 additions and 85 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "framed" 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<usize> # was: pub fn max_encoded_len(usize) -> Result<usize>
# now: pub const fn max_encoded_len(usize) -> usize # now: pub const fn max_encoded_len(usize) -> usize
version = "0.1.4" version = "0.1.4"

View file

@ -1,4 +1,6 @@
//! Vec-backed FIFO buffer of bytes for testing. //! Vec-backed FIFO buffer of bytes for testing.
//!
//! TODO: Remove this, I have an easy workaround.
use std::cell::RefCell; use std::cell::RefCell;
use std::io::{Read, Result, Write}; use std::io::{Read, Result, Write};

View file

@ -34,16 +34,17 @@
//! //!
//! `use_std`: Use standard library. Enabled by default, disable for no_std. //! `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 //! `typed`: Enables the [`typed`](typed/index.html) sub-module for sending and
//! receiving structs serialized with serde. Enabled by default. //! 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 //! ## Byte slice wrapper types
//! mixing them up. You are encouraged to use these types in your own //!
//! code when integrating with this crate. Definitions: //! 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 //! ```rust,ignore
//! /// Arbitrary user data. //! /// Arbitrary user data.
@ -66,12 +67,6 @@
//! pub struct BoxEncoded(_); //! 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 //! Note that data typed as `Payload` or `Encoded` may be
//! efficiently passed as a function argument by reference, but is //! efficiently passed as a function argument by reference, but is
//! returned using an opaque struct (`BoxPayload`, `BoxEncoded`) //! returned using an opaque struct (`BoxPayload`, `BoxEncoded`)
@ -79,17 +74,67 @@
//! and `decode_*` variants that require this are only available with //! and `decode_*` variants that require this are only available with
//! the `use_std` Cargo feature. //! 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 //! ## 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 //! ## 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)] #![deny(warnings)]
@ -243,7 +288,7 @@ pub fn encode_to_box(p: &Payload) -> Result<BoxEncoded> {
/// Encode the supplied payload data as a frame and write it to the /// Encode the supplied payload data as a frame and write it to the
/// supplied `Write`. /// 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. /// so if this is required.
/// ///
/// Returns the length of the frame it has written. /// Returns the length of the frame it has written.
@ -278,8 +323,8 @@ pub fn encode_to_writer<W: Write>(p: &Payload, w: &mut W) -> Result<usize> {
/// # Panics /// # Panics
/// ///
/// This function will panic if `dest` is not large enough for the decoded frame. /// This function will panic if `dest` is not large enough for the decoded frame.
/// Ensure `dest.len() >= max_decoded_len(e.len())?`. /// Ensure `dest.len() >= e.len()`.
pub fn decode_to_slice(e: &Encoded, mut dest: &mut [u8]) pub fn decode_to_slice(e: &Encoded, dest: &mut [u8])
-> Result<usize> { -> Result<usize> {
#[cfg(feature = "trace")] { #[cfg(feature = "trace")] {
println!("framed: Encoded input = {:?}", e); println!("framed: Encoded input = {:?}", e);
@ -293,19 +338,19 @@ pub fn decode_to_slice(e: &Encoded, mut dest: &mut [u8])
return Err(Error::EofDuringFrame) 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); assert_eq!(e[e.len() - 1], FRAME_END_SYMBOL);
// Just the body (COBS-encoded payload). // Just the body (COBS-encoded payload).
let body = &e[0..(e.len()-1)]; let body = &e[0..(e.len()-1)];
let len = cobs::decode(body, &mut dest) let len = cobs::decode(body, dest)
.map_err(|_| Error::CobsDecodeFailed)?; .map_err(|_| Error::CobsDecodeFailed)?;
#[cfg(feature = "trace")] { #[cfg(feature = "trace")] {
println!("framed: body = {:?}\n\ println!("framed: body = {:?}\n\
framed: decoded = {:?}", framed: decoded = {:?}",
body, &dest[0..len]); body, dest[0..len]);
} }
Ok(len) Ok(len)
@ -317,7 +362,7 @@ pub fn decode_to_box(e: &Encoded) -> Result<BoxPayload> {
if e.len() == 0 { if e.len() == 0 {
return Err(Error::EofBeforeFrame); 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)?; let len = decode_to_slice(e, &mut buf)?;
buf.truncate(len); buf.truncate(len);
Ok(BoxPayload::from(buf)) Ok(BoxPayload::from(buf))
@ -365,16 +410,11 @@ pub fn decode_from_reader<R: Read>(r: &mut Read) -> Result<BoxPayload> {
/// within a frame with the encoded length supplied. /// within a frame with the encoded length supplied.
/// ///
/// Useful for calculating an appropriate buffer length. /// Useful for calculating an appropriate buffer length.
pub fn max_decoded_len(code_len: usize) -> Result<usize> { pub const fn max_decoded_len(code_len: usize) -> usize {
let framing_len = HEADER_LEN + FOOTER_LEN; // This is an over-estimate of the required decoded buffer, but
if code_len < framing_len { // wasting HEADER_LEN + FOOTER_LEN bytes should be acceptable and
return Err(Error::EncodedFrameTooShort) // we can calculate this trivially in a const fn.
} code_len
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 /// 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 + 1
} }
/// Sends encoded frames over an inner `io::Write` instance. /// Sends encoded frames over an inner `std::io::Write` instance.
#[cfg(feature = "use_std")] #[cfg(feature = "use_std")]
pub struct Sender<W: Write> { pub struct Sender<W: Write> {
w: W, w: W,
@ -430,15 +470,14 @@ pub struct Sender<W: Write> {
#[cfg(feature = "use_std")] #[cfg(feature = "use_std")]
impl<W: Write> Sender<W> { impl<W: Write> Sender<W> {
/// Construct a `Sender` that sends frames over the supplied /// Construct a `Sender` that writes encoded frames to `w`.
/// `io::Write`.
pub fn new(w: W) -> Sender<W> { pub fn new(w: W) -> Sender<W> {
Sender::<W> { Sender::<W> {
w: w, 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 { pub fn into_inner(self) -> W {
self.w self.w
} }
@ -474,7 +513,7 @@ impl<W: Write> Sender<W> {
} }
} }
/// Receives encoded frames from an inner `io::Read` instance. /// Receives encoded frames from an inner `std::io::Read` instance.
#[cfg(feature = "use_std")] #[cfg(feature = "use_std")]
pub struct Receiver<R: Read> { pub struct Receiver<R: Read> {
r: R, r: R,
@ -483,7 +522,7 @@ pub struct Receiver<R: Read> {
#[cfg(feature = "use_std")] #[cfg(feature = "use_std")]
impl<R: Read> Receiver<R> { impl<R: Read> Receiver<R> {
/// Construct a `Receiver` that receives frames from the supplied /// Construct a `Receiver` that receives frames from the supplied
/// `io::Read`. /// `std::io::Read`.
pub fn new(r: R) -> Receiver<R> { pub fn new(r: R) -> Receiver<R> {
Receiver::<R> { Receiver::<R> {
r: r, r: r,
@ -515,20 +554,13 @@ mod tests {
assert_eq!(max_encoded_len(255), 258); 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] #[test]
fn max_decoded_len_ok() { fn max_decoded_len_ok() {
assert_eq!(max_decoded_len(1) .unwrap(), 0); assert_eq!(max_decoded_len(0) , 0);
assert_eq!(max_decoded_len(2) .unwrap(), 1); assert_eq!(max_decoded_len(1) , 1);
assert_eq!(max_decoded_len(3) .unwrap(), 2); assert_eq!(max_decoded_len(2) , 2);
assert_eq!(max_decoded_len(255).unwrap(), 254); assert_eq!(max_decoded_len(3) , 3);
assert_eq!(max_decoded_len(255), 255);
} }
} }

View file

@ -1,14 +1,16 @@
//! Sending and receiving structs serialized with serde and //! Sending and receiving serde-serialized structs.
//! [`ssmarshal`][ssmarshal]. //!
//! 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]: 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 //! ## Example usage from a `std` crate
//! //!
//! The `Sender` struct writes serialized and encoded values to an //! The `Sender` struct writes serialized and encoded values to an
@ -31,28 +33,28 @@
//! b: u16, //! 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); //! let mut sender = Sender::<_, Test>::new(&mut encoded);
//! sender.send(&input).expect("send ok"); //! 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 mut receiver = Receiver::<_, Test>::new(Cursor::new(encoded));
//! let output = receiver.recv().expect("recv ok"); //! let decoded = receiver.recv().expect("recv ok");
//! //!
//! assert_eq!(input, output); //! assert_eq!(payload, decoded);
//! # } //! # }
//! ``` //! ```
//! //!
//! ## Example usage from a `no_std` crate //! ## Example usage from a `no_std` crate
//! //!
//! The `encode_to_slice` and `decode_from_slice` functions offer an //! 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 and
//! available and cannot use `std::io::Read` or `std::io::Write`. //! cannot use `std::io::Read` or `std::io::Write`.
//! //!
//! ```rust //! ```rust
//! # extern crate framed; //! # extern crate framed;
@ -68,12 +70,12 @@
//! # b: u16, //! # 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::<Test>()]; //! let mut ser_buf = [0u8; max_serialize_buf_len::<Test>()];
//! let mut encoded_buf = [0u8; max_encoded_len::<Test>()]; //! let mut encoded_buf = [0u8; max_encoded_len::<Test>()];
//! let encoded_len = encode_to_slice::<Test>( //! let encoded_len = encode_to_slice::<Test>(
//! &input, //! &payload,
//! &mut ser_buf, //! &mut ser_buf,
//! &mut encoded_buf //! &mut encoded_buf
//! ).expect("encode ok"); //! ).expect("encode ok");
@ -82,10 +84,10 @@
//! // `encoded` now contains the complete encoded frame. //! // `encoded` now contains the complete encoded frame.
//! //!
//! let mut de_buf = [0u8; max_serialize_buf_len::<Test>()]; //! let mut de_buf = [0u8; max_serialize_buf_len::<Test>()];
//! let output = decode_from_slice(encoded, &mut de_buf) //! let decoded = decode_from_slice(encoded, &mut de_buf)
//! .expect("decode ok"); //! .expect("decode ok");
//! //!
//! assert_eq!(input, output); //! assert_eq!(payload, decoded);
//! # } //! # }
//! ``` //! ```
@ -170,7 +172,7 @@ pub const fn max_encoded_len<T: DeserializeOwned + Serialize>() -> usize {
/// length needed by `encode_to_slice` and `decode_from_slice` when /// length needed by `encode_to_slice` and `decode_from_slice` when
/// serializing or deserializing a value of type `T`. /// serializing or deserializing a value of type `T`.
pub const fn max_serialize_buf_len<T: DeserializeOwned + Serialize>() -> usize { pub const fn max_serialize_buf_len<T: DeserializeOwned + Serialize>() -> usize {
size_of::<T>() super::max_encoded_len(size_of::<T>())
} }
@ -374,17 +376,17 @@ mod tests {
#[test] #[test]
fn serialize_buf_len() { fn serialize_buf_len() {
assert_eq!(max_serialize_buf_len::<Test>(), 40); assert_eq!(max_serialize_buf_len::<Test>(), 42);
assert_eq!(max_serialize_buf_len::<u8>(), 1); assert_eq!(max_serialize_buf_len::<u8>(), 3);
assert_eq!(max_serialize_buf_len::<u16>(), 2); assert_eq!(max_serialize_buf_len::<u16>(), 4);
assert_eq!(max_serialize_buf_len::<u32>(), 4); assert_eq!(max_serialize_buf_len::<u32>(), 6);
assert_eq!(max_serialize_buf_len::<u64>(), 8); assert_eq!(max_serialize_buf_len::<u64>(), 10);
} }
#[test] #[test]
fn encoded_len() { fn encoded_len() {
assert_eq!(max_encoded_len::<Test>(), 42); assert_eq!(max_encoded_len::<Test>(), 44);
assert_eq!(max_encoded_len::<u8>(), 3); assert_eq!(max_encoded_len::<u8>(), 5);
} }
} }