Add framed::typed::encode_to_slice().
Also write framed::typed::max_*_len().
This commit is contained in:
parent
42b75a7551
commit
b5cdc7b86f
3 changed files with 229 additions and 48 deletions
|
@ -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>"]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue