1
Fork 0

Add framed::typed::encode_to_slice().

Also write framed::typed::max_*_len().
This commit is contained in:
Alex Helfet 2017-12-28 17:01:24 +00:00
parent 42b75a7551
commit b5cdc7b86f
3 changed files with 229 additions and 48 deletions

View file

@ -1,5 +1,8 @@
[package]
name = "framed"
# Next version is 0.2.0. I changed the type of max_encoded_len
# was: pub fn max_encoded_len(usize) -> Result<usize>
# now: pub const fn max_encoded_len(usize) -> usize
version = "0.1.4"
description = "Send and receive data over lossy streams of bytes."
authors = ["Alex Helfet <alex.helfet@gmail.com>"]

View file

@ -53,6 +53,10 @@
//! /// 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(_);
@ -82,8 +86,15 @@
#![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")]
@ -125,6 +136,10 @@ pub type Payload = [u8];
/// 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<u8> as that's
// what `cobs` returns us and converting them into a Box<[u8]> (with
// Vec::into_boxed_slice(self)) would require re-allocation.
@ -193,9 +208,9 @@ const FOOTER_LEN: usize = 1;
///
/// 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 [u8]) -> Result<usize> {
pub fn encode_to_slice(p: &Payload, dest: &mut Encoded) -> Result<usize> {
// Panic if code won't fit in `dest` because this is a programmer error.
assert!(max_encoded_len(p.len())? <= dest.len());
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;
@ -210,7 +225,7 @@ pub fn encode_to_slice(p: &Payload, dest: &mut [u8]) -> Result<usize> {
/// 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<BoxEncoded> {
let mut buf = vec![0; max_encoded_len(p.len())?];
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))
@ -337,8 +352,10 @@ pub fn decode_from_reader<R: Read>(r: &mut Read) -> Result<BoxPayload> {
decode_to_box(&*next_frame)
}
/// Returns the maximum possible decoded length given a frame with
/// the encoded length supplied.
/// 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<usize> {
let framing_len = HEADER_LEN + FOOTER_LEN;
if code_len < framing_len {
@ -351,14 +368,50 @@ pub fn max_decoded_len(code_len: usize) -> Result<usize> {
Ok(cobs_decode_limit)
}
/// Returns the maximum possible encoded length for a frame with
/// Returns an upper bound for the encoded length of a frame with
/// the payload length supplied.
pub fn max_encoded_len(payload_len: usize) -> Result<usize> {
Ok(HEADER_LEN
+ cobs::max_encoding_length(payload_len)
+ FOOTER_LEN)
///
/// 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")]
@ -446,11 +499,11 @@ mod tests {
#[test]
fn max_encoded_len_ok() {
assert_eq!(max_encoded_len(0) .unwrap(), 1);
assert_eq!(max_encoded_len(1) .unwrap(), 3);
assert_eq!(max_encoded_len(2) .unwrap(), 4);
assert_eq!(max_encoded_len(254).unwrap(), 256);
assert_eq!(max_encoded_len(255).unwrap(), 258);
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]

View file

@ -8,23 +8,83 @@
//! 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.
//!
//! This module currently requires `std`, the standard library.
use ::{Encoded, TempBuffer};
use error::{Result};
use serde::Serialize;
use serde::de::DeserializeOwned;
use ssmarshal;
#[cfg(feature = "use_std")]
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::mem::size_of;
use core::marker::PhantomData;
use core::mem::size_of;
/// Serializes and encodes the supplied value `v` into destination
/// buffer `dest`, using `ser_buf` as a temporary serialization buffer.
///
/// # Examples
///
/// ```rust
/// extern crate framed;
/// use framed::typed::*;
/// extern crate serde;
/// #[macro_use]
/// extern crate serde_derive;
///
/// #[derive(Serialize)]
/// struct Test {
/// a: u8,
/// b: u16,
/// }
///
/// fn main() {
/// let mut ser_buf = [0u8; max_serialize_buf_len::<Test>()];
/// let mut encoded = [0u8; max_encoded_len::<Test>()];
/// encode_to_slice::<Test>(
/// &Test { a: 1, b: 2 },
/// &mut ser_buf,
/// &mut encoded
/// ).expect("encode ok");
/// }
/// ```
///
pub fn encode_to_slice<T: Serialize>(
v: &T,
ser_buf: &mut TempBuffer,
dest: &mut Encoded,
) -> Result<usize> {
assert!(ser_buf.len() >= max_serialize_buf_len::<T>());
assert!(dest.len() >= max_encoded_len::<T>());
let ser_len = ssmarshal::serialize(ser_buf, v)?;
let ser = &ser_buf[0..ser_len];
super::encode_to_slice(ser, dest)
}
/// Returns an upper bound for the encoded length of a frame with a
/// serialized `T` value as its payload.
///
/// Useful for calculating an appropriate buffer length.
pub const fn max_encoded_len<T: Serialize>() -> usize {
super::max_encoded_len(max_serialize_buf_len::<T>())
}
/// Returns an upper bound for the temporary serialization buffer
/// length needed by `encode_to_slice` when serializing a
/// value of type `T`.
pub const fn max_serialize_buf_len<T: Serialize>() -> usize {
size_of::<T>()
}
/// Sends encoded structs of type `T` over an inner `io::Write` instance.
#[cfg(feature = "use_std")]
pub struct Sender<W: Write, T: Serialize> {
w: W,
_t: PhantomData<T>,
}
#[cfg(feature = "use_std")]
impl<W: Write, T: Serialize> Sender<W, T> {
/// Construct a `Sender` that sends encoded structs over the supplied
/// `io::Write`.
@ -54,6 +114,8 @@ impl<W: Write, T: Serialize> Sender<W, T> {
///
/// See also: [`send`](#method.send)
pub fn queue(&mut self, v: &T) -> Result<usize> {
// TODO: Re-use encode_to_slice
// This uses a dynamically allocated buffer.
//
// I couldn't get a no_std version to compile with a stack-allocated
@ -84,7 +146,7 @@ impl<W: Write, T: Serialize> Sender<W, T> {
#[cfg(feature = "trace")] {
println!("framed: Serialized = {:?}", ser);
}
super::encode_to_writer(&ser, &mut self.w)
::encode_to_writer(&ser, &mut self.w)
}
/// Encode the supplied payload as a frame, write it to the
@ -102,11 +164,13 @@ impl<W: Write, T: Serialize> Sender<W, T> {
}
/// Receives encoded structs of type `T` from an inner `io::Read` instance.
#[cfg(feature = "use_std")]
pub struct Receiver<R: Read, T: DeserializeOwned> {
r: R,
_t: PhantomData<T>,
}
#[cfg(feature = "use_std")]
impl<R: Read, T: DeserializeOwned> Receiver<R, T> {
/// Construct a `Receiver` that receives encoded structs from the supplied
/// `io::Read`.
@ -133,9 +197,6 @@ impl<R: Read, T: DeserializeOwned> Receiver<R, T> {
#[cfg(test)]
mod tests {
use channel::Channel;
use error::Error;
use std::io::{Read, Write};
use super::*;
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
@ -156,26 +217,7 @@ mod tests {
a: [u8; 3],
}
#[test]
fn one() {
let (mut tx, mut rx) = pair();
let v = val();
tx.send(&v).unwrap();
let r = rx.recv().unwrap();
println!("r: {:#?}", r);
assert_eq!(v, r);
}
#[test]
fn empty_input() {
let (mut _tx, mut rx) = pair();
match rx.recv() {
Err(Error::EofBeforeFrame) => (),
e @ _ => panic!("Bad value: {:?}", e)
}
}
fn val() -> Test {
fn test_val() -> Test {
let v = Test {
i8: 1,
i16: 2,
@ -196,10 +238,93 @@ mod tests {
v
}
fn pair() -> (Sender<Box<Write>, Test>, Receiver<Box<Read>, Test>) {
let c = Channel::new();
let tx = Sender::new(Box::new(c.writer()) as Box<Write>);
let rx = Receiver::new(Box::new(c.reader()) as Box<Read>);
(tx, rx)
mod slice_tests {
use super::*;
#[test]
fn first() {
let mut ser_buf = [0u8; 100];
let mut enc_buf = [0u8; 100];
let len = super::encode_to_slice(
&test_val(), &mut ser_buf, &mut enc
).unwrap();
let enc = &enc_buf[0..len];
super::decode_from_slice(&enc
// TODO: Deserialize the test value.
}
#[test]
#[should_panic]
fn bad_ser_buf_len() {
let mut ser_buf = [0u8; 1];
let mut enc = [0u8; 100];
super::encode_to_slice(
&test_val(), &mut ser_buf, &mut enc
).unwrap();
}
#[test]
#[should_panic]
fn bad_dest_len() {
let mut ser_buf = [0u8; 100];
let mut enc = [0u8; 1];
super::encode_to_slice(
&test_val(), &mut ser_buf, &mut enc
).unwrap();
}
}
mod len_tests {
use super::*;
#[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);
}
#[test]
fn encoded_len() {
assert_eq!(max_encoded_len::<Test>(), 42);
assert_eq!(max_encoded_len::<u8>(), 3);
}
}
#[cfg(feature = "use_std")]
mod rw_tests {
use channel::Channel;
use error::Error;
use std::io::{Read, Write};
use super::*;
#[test]
fn one() {
let (mut tx, mut rx) = pair();
let v = test_val();
tx.send(&v).unwrap();
let r = rx.recv().unwrap();
println!("r: {:#?}", r);
assert_eq!(v, r);
}
#[test]
fn empty_input() {
let (mut _tx, mut rx) = pair();
match rx.recv() {
Err(Error::EofBeforeFrame) => (),
e @ _ => panic!("Bad value: {:?}", e)
}
}
fn pair() -> (Sender<Box<Write>, Test>, Receiver<Box<Read>, Test>) {
let c = Channel::new();
let tx = Sender::new(Box::new(c.writer()) as Box<Write>);
let rx = Receiver::new(Box::new(c.reader()) as Box<Read>);
(tx, rx)
}
}
}