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]
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>
# now: pub const fn max_encoded_len(usize) -> usize
version = "0.1.4"

View file

@ -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};

View file

@ -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<BoxEncoded> {
/// 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<W: Write>(p: &Payload, w: &mut W) -> Result<usize> {
/// # 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<usize> {
#[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<BoxPayload> {
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: Read>(r: &mut Read) -> Result<BoxPayload> {
/// within a frame with the encoded length supplied.
///
/// Useful for calculating an appropriate buffer length.
pub fn max_decoded_len(code_len: usize) -> Result<usize> {
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: Write> {
w: W,
@ -430,15 +470,14 @@ pub struct Sender<W: Write> {
#[cfg(feature = "use_std")]
impl<W: Write> Sender<W> {
/// 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<W> {
Sender::<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 {
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")]
pub struct Receiver<R: Read> {
r: R,
@ -483,7 +522,7 @@ pub struct Receiver<R: Read> {
#[cfg(feature = "use_std")]
impl<R: Read> Receiver<R> {
/// Construct a `Receiver` that receives frames from the supplied
/// `io::Read`.
/// `std::io::Read`.
pub fn new(r: R) -> Receiver<R> {
Receiver::<R> {
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);
}
}

View file

@ -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::<Test>()];
//! let mut encoded_buf = [0u8; max_encoded_len::<Test>()];
//! let encoded_len = encode_to_slice::<Test>(
//! &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::<Test>()];
//! let output = decode_from_slice(encoded, &mut de_buf)
//! 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<T: DeserializeOwned + Serialize>() -> 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<T: DeserializeOwned + Serialize>() -> usize {
size_of::<T>()
super::max_encoded_len(size_of::<T>())
}
@ -374,17 +376,17 @@ mod tests {
#[test]
fn serialize_buf_len() {
assert_eq!(max_serialize_buf_len::<Test>(), 40);
assert_eq!(max_serialize_buf_len::<u8>(), 1);
assert_eq!(max_serialize_buf_len::<u16>(), 2);
assert_eq!(max_serialize_buf_len::<u32>(), 4);
assert_eq!(max_serialize_buf_len::<u64>(), 8);
assert_eq!(max_serialize_buf_len::<Test>(), 42);
assert_eq!(max_serialize_buf_len::<u8>(), 3);
assert_eq!(max_serialize_buf_len::<u16>(), 4);
assert_eq!(max_serialize_buf_len::<u32>(), 6);
assert_eq!(max_serialize_buf_len::<u64>(), 10);
}
#[test]
fn encoded_len() {
assert_eq!(max_encoded_len::<Test>(), 42);
assert_eq!(max_encoded_len::<u8>(), 3);
assert_eq!(max_encoded_len::<Test>(), 44);
assert_eq!(max_encoded_len::<u8>(), 5);
}
}