Moved all encoding and decoding functions onto Codec methods that contain configuration set with a builder pattern on Config structs.
Part of issue #24.
This commit is contained in:
parent
311040f46c
commit
dd17f1f65d
8 changed files with 586 additions and 349 deletions
|
@ -14,7 +14,6 @@ mod error;
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
|
|
||||||
use clap::Arg;
|
use clap::Arg;
|
||||||
use framed::typed::Receiver;
|
|
||||||
use std::io::{stdin, stdout};
|
use std::io::{stdin, stdout};
|
||||||
|
|
||||||
arg_enum! {
|
arg_enum! {
|
||||||
|
@ -47,7 +46,9 @@ fn try() -> Result<()> {
|
||||||
let matches = app.get_matches();
|
let matches = app.get_matches();
|
||||||
let out_fmt = value_t!(matches, "out-format", OutputFormat)?;
|
let out_fmt = value_t!(matches, "out-format", OutputFormat)?;
|
||||||
|
|
||||||
let mut r = Receiver::<_, UserType>::new(stdin());
|
let mut r = framed::bytes::Config::default()
|
||||||
|
.typed::<UserType>()
|
||||||
|
.into_receiver(stdin());
|
||||||
|
|
||||||
let mut csvw: Option<csv::Writer<_>> =
|
let mut csvw: Option<csv::Writer<_>> =
|
||||||
match out_fmt {
|
match out_fmt {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "framed"
|
name = "framed"
|
||||||
|
# Next version is 0.4.0 after moving functions onto Codec structs.
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
description = "Send and receive data over lossy streams of bytes."
|
description = "Send and receive data over lossy streams of bytes."
|
||||||
authors = ["Alex Helfet <alex.helfet@gmail.com>"]
|
authors = ["Alex Helfet <alex.helfet@gmail.com>"]
|
||||||
|
|
|
@ -14,17 +14,19 @@
|
||||||
//! # use framed::bytes::*;
|
//! # use framed::bytes::*;
|
||||||
//! # use std::io::Cursor;
|
//! # use std::io::Cursor;
|
||||||
//! #
|
//! #
|
||||||
|
//! let config = Config::default();
|
||||||
|
//!
|
||||||
//! let payload = [1, 2, 3];
|
//! let payload = [1, 2, 3];
|
||||||
//!
|
//!
|
||||||
//! let mut encoded = vec![];
|
//! let mut encoded = vec![];
|
||||||
//! {
|
//! {
|
||||||
//! let mut sender = Sender::new(&mut encoded);
|
//! let mut sender = config.clone().into_sender(&mut encoded);
|
||||||
//! sender.send(&payload).expect("send ok");
|
//! sender.send(&payload).expect("send ok");
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! // `encoded` now contains the encoded frame.
|
//! // `encoded` now contains the encoded frame.
|
||||||
//!
|
//!
|
||||||
//! let mut receiver = Receiver::new(Cursor::new(encoded));
|
//! let mut receiver = config.clone().into_receiver(Cursor::new(encoded));
|
||||||
//! let decoded = receiver.recv().expect("recv ok");
|
//! let decoded = receiver.recv().expect("recv ok");
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(payload, *decoded);
|
//! assert_eq!(payload, *decoded);
|
||||||
|
@ -33,13 +35,15 @@
|
||||||
//! ## 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
|
||||||
//! available and cannot use `std::io::Read` or `std::io::Write`.
|
//! available and cannot use `std::io::Read` or `std::io::Write`.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! # use framed::*;
|
//! # use framed::*;
|
||||||
//! # use framed::bytes::*;
|
//! # use framed::bytes::*;
|
||||||
//! #
|
//! #
|
||||||
|
//! let mut codec = Config::default().into_codec();
|
||||||
|
//!
|
||||||
//! // In a no_std crate without dynamic memory allocation we would typically
|
//! // In a no_std crate without dynamic memory allocation we would typically
|
||||||
//! // know the maximum payload length, which we can use for payload buffers.
|
//! // know the maximum payload length, which we can use for payload buffers.
|
||||||
//! const MAX_PAYLOAD_LEN: usize = 10;
|
//! const MAX_PAYLOAD_LEN: usize = 10;
|
||||||
|
@ -56,13 +60,15 @@
|
||||||
//! assert!(payload.len() <= MAX_PAYLOAD_LEN);
|
//! assert!(payload.len() <= MAX_PAYLOAD_LEN);
|
||||||
//!
|
//!
|
||||||
//! let mut encoded_buf = [0u8; MAX_FRAME_LEN];
|
//! let mut encoded_buf = [0u8; MAX_FRAME_LEN];
|
||||||
//! let encoded_len = encode_to_slice(&payload, &mut encoded_buf).expect("encode ok");
|
//! let encoded_len = codec.encode_to_slice(&payload, &mut encoded_buf)
|
||||||
|
//! .expect("encode ok");
|
||||||
//! let encoded = &encoded_buf[0..encoded_len];
|
//! let encoded = &encoded_buf[0..encoded_len];
|
||||||
//!
|
//!
|
||||||
//! // `encoded` now contains the encoded frame.
|
//! // `encoded` now contains the encoded frame.
|
||||||
//!
|
//!
|
||||||
//! let mut decoded_buf = [0u8; MAX_PAYLOAD_LEN];
|
//! let mut decoded_buf = [0u8; MAX_PAYLOAD_LEN];
|
||||||
//! let decoded_len = decode_to_slice(&encoded, &mut decoded_buf).expect("decode ok");
|
//! let decoded_len = codec.decode_to_slice(&encoded, &mut decoded_buf)
|
||||||
|
//! .expect("decode ok");
|
||||||
//! let decoded = &decoded_buf[0..decoded_len];
|
//! let decoded = &decoded_buf[0..decoded_len];
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(payload, *decoded);
|
//! assert_eq!(payload, *decoded);
|
||||||
|
@ -73,10 +79,12 @@
|
||||||
use ::{Payload, Encoded, FRAME_END_SYMBOL};
|
use ::{Payload, Encoded, FRAME_END_SYMBOL};
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
use ::{BoxPayload, BoxEncoded};
|
use ::{BoxPayload, BoxEncoded};
|
||||||
|
use ::checksum::MAX_CHECKSUM_LEN;
|
||||||
use ::error::{Error, Result};
|
use ::error::{Error, Result};
|
||||||
use byteorder::{self, ByteOrder};
|
use ::typed;
|
||||||
use cobs;
|
use cobs;
|
||||||
use crc16;
|
use serde::Serialize;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
use ref_slice::ref_slice_mut;
|
use ref_slice::ref_slice_mut;
|
||||||
|
@ -84,222 +92,301 @@ use ref_slice::ref_slice_mut;
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
const HEADER_LEN: usize = 0;
|
pub use ::checksum::Checksum;
|
||||||
|
|
||||||
const CHECKSUM_LEN: usize = 2;
|
/// Contains methods for encoding and decoding byte slices with a
|
||||||
|
/// specific configuration.
|
||||||
const FOOTER_LEN: usize = CHECKSUM_LEN + 1;
|
///
|
||||||
|
/// Construct an instance from a `Config` instance with the
|
||||||
const FRAMING_LEN: usize = HEADER_LEN + FOOTER_LEN;
|
/// `Config::into_codec` method.
|
||||||
|
pub struct Codec {
|
||||||
fn checksum(p: &Payload) -> [u8; CHECKSUM_LEN] {
|
config: Config
|
||||||
let u16 = crc16::State::<crc16::CDMA2000>::calculate(p);
|
|
||||||
let mut bytes = [0u8; CHECKSUM_LEN];
|
|
||||||
byteorder::NetworkEndian::write_u16(&mut bytes, u16);
|
|
||||||
bytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the supplied payload data as a frame at the beginning of
|
/// Configurable options for encoding and decoding byte slices using a
|
||||||
/// the supplied buffer `dest`. Available from `no_std` crates.
|
/// builder pattern.
|
||||||
///
|
///
|
||||||
/// Returns the number of bytes it has written to the buffer.
|
/// Construct an instance with `Config::default()`.
|
||||||
///
|
#[derive(Clone, Debug, Default)]
|
||||||
/// # Panics
|
pub struct Config {
|
||||||
///
|
checksum: Checksum,
|
||||||
/// 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 Encoded) -> Result<usize> {
|
|
||||||
// Panic if encoded frame won't fit in `dest` because this is a
|
|
||||||
// programmer error.
|
|
||||||
assert!(max_encoded_len(p.len()) <= dest.len());
|
|
||||||
|
|
||||||
#[cfg(feature = "trace")] {
|
impl Config {
|
||||||
println!("framed::encode: Payload = {:?}", p);
|
/// Construct a `Codec` instance with this configuration.
|
||||||
|
pub fn into_codec(self) -> Codec {
|
||||||
|
Codec {
|
||||||
|
config: self,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cobs_len = cobs::encode(&p, &mut dest[HEADER_LEN..]);
|
#[cfg(feature = "use_std")]
|
||||||
let checksum = checksum(p);
|
/// Construct a `Receiver` instance with this configuration.
|
||||||
|
pub fn into_receiver<R: Read>(self, r: R) -> Receiver<R> {
|
||||||
|
Receiver::<R> {
|
||||||
|
codec: self.into_codec(),
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
#[cfg(feature = "use_std")]
|
||||||
let mut _header = &mut dest[0..HEADER_LEN];
|
/// Construct a `Sender` instance with this configuration.
|
||||||
|
pub fn into_sender<W: Write>(self, w: W) -> Sender<W> {
|
||||||
|
Sender::<W> {
|
||||||
|
codec: self.into_codec(),
|
||||||
|
w: w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a `framed::typed::Config` instance to encode and decode a
|
||||||
|
/// serializable type `T` with this byte encoding configuration.
|
||||||
|
pub fn typed<T: DeserializeOwned + Serialize>(self) -> typed::Config<T> {
|
||||||
|
typed::Config::<T>::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current checksum configuration.
|
||||||
|
pub fn checksum(&self) -> &Checksum {
|
||||||
|
&self.checksum
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the checksum configuration.
|
||||||
|
pub fn set_checksum(&mut self, checksum: Checksum) -> &mut Self {
|
||||||
|
self.checksum = checksum;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_HEADER_LEN: usize = 0;
|
||||||
|
|
||||||
|
const MAX_FOOTER_LEN: usize = MAX_CHECKSUM_LEN + 1;
|
||||||
|
|
||||||
|
const MAX_FRAMING_LEN: usize = MAX_HEADER_LEN + MAX_FOOTER_LEN;
|
||||||
|
|
||||||
|
impl Codec {
|
||||||
|
fn checksum(&self) -> &Checksum {
|
||||||
|
&self.config.checksum
|
||||||
|
}
|
||||||
|
|
||||||
|
fn header_len(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn footer_len(&self) -> usize {
|
||||||
|
1 + self.checksum().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn framing_len(&self) -> usize {
|
||||||
|
self.header_len() + self.footer_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode the supplied payload data as a frame at the beginning of
|
||||||
|
/// the supplied buffer `dest`. Available from `no_std` crates.
|
||||||
|
///
|
||||||
|
/// Returns the number of bytes it has written to the buffer.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// 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(&mut self, p: &Payload, dest: &mut Encoded)
|
||||||
|
-> Result<usize> {
|
||||||
|
// Panic if encoded frame won't fit in `dest` because this is a
|
||||||
|
// programmer error.
|
||||||
|
assert!(max_encoded_len(p.len()) <= dest.len());
|
||||||
|
|
||||||
#[cfg(feature = "trace")] {
|
#[cfg(feature = "trace")] {
|
||||||
println!("framed::encode: Header = {:?}", _header);
|
println!("framed::encode: Payload = {:?}", p);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
{
|
let cobs_len = cobs::encode(&p, &mut dest[self.header_len()..]);
|
||||||
let footer = &mut dest[
|
let checksum = self.checksum();
|
||||||
(HEADER_LEN + cobs_len)
|
let checksum_value = checksum.calculate(p);
|
||||||
..
|
let checksum_len = checksum.len();
|
||||||
(HEADER_LEN + cobs_len + FOOTER_LEN)];
|
|
||||||
footer[0..CHECKSUM_LEN].copy_from_slice(&checksum);
|
{
|
||||||
footer[CHECKSUM_LEN] = FRAME_END_SYMBOL;
|
let mut _header = &mut dest[0..self.header_len()];
|
||||||
|
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed::encode: Header = {:?}", _header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let footer = &mut dest[
|
||||||
|
(self.header_len() + cobs_len)
|
||||||
|
..
|
||||||
|
(self.header_len() + cobs_len + self.footer_len())];
|
||||||
|
footer[0..checksum_len].copy_from_slice(&*checksum_value);
|
||||||
|
footer[checksum_len] = FRAME_END_SYMBOL;
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed::encode: Footer = {:?}", footer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let len = self.header_len() + cobs_len + self.footer_len();
|
||||||
#[cfg(feature = "trace")] {
|
#[cfg(feature = "trace")] {
|
||||||
println!("framed::encode: Footer = {:?}", footer);
|
println!("framed::encode: Frame = {:?}", &dest[0..len]);
|
||||||
}
|
}
|
||||||
}
|
Ok(len)
|
||||||
let len = HEADER_LEN + cobs_len + FOOTER_LEN;
|
|
||||||
#[cfg(feature = "trace")] {
|
|
||||||
println!("framed::encode: Frame = {:?}", &dest[0..len]);
|
|
||||||
}
|
|
||||||
Ok(len)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 len = encode_to_slice(p, &mut *buf)?;
|
|
||||||
buf.truncate(len);
|
|
||||||
Ok(BoxEncoded::from(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 must do
|
|
||||||
/// so if this is required.
|
|
||||||
///
|
|
||||||
/// Returns the length of the frame it has written.
|
|
||||||
#[cfg(feature = "use_std")]
|
|
||||||
pub fn encode_to_writer<W: Write>(p: &Payload, w: &mut W) -> Result<usize> {
|
|
||||||
let b = encode_to_box(p)?;
|
|
||||||
w.write_all(&*b.0)?;
|
|
||||||
Ok(b.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decode the supplied encoded frame, placing the payload at the
|
|
||||||
/// beginning of the supplied buffer `dest`. Available from `no_std` crates.
|
|
||||||
///
|
|
||||||
/// When reading from a stream, the caller can continue reading data
|
|
||||||
/// and buffering it until a `FRAME_END_SYMBOL` is read, then pass the
|
|
||||||
/// whole buffer including `FRAME_END_SYMBOL` to this function for
|
|
||||||
/// decoding.
|
|
||||||
///
|
|
||||||
/// If there is more than 1 FRAME_END_SYMBOL within `e`, the result
|
|
||||||
/// is undefined. Make sure you only pass 1 frame at a time.
|
|
||||||
///
|
|
||||||
/// Returns the length of the payload it has decoded.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns `Err(Error::EofDuringFrame`) if `e` contains >= 1 bytes of
|
|
||||||
/// a frame, but not a complete frame. A complete frame should have
|
|
||||||
/// `FRAME_END_SYMBOL` as the last byte.
|
|
||||||
///
|
|
||||||
/// Returns `Err(Error::EofBeforeFrame`) if `e.len()` is 0.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This function will panic if `dest` is not large enough for the decoded frame.
|
|
||||||
/// Ensure `dest.len() >= e.len()`.
|
|
||||||
pub fn decode_to_slice(e: &Encoded, dest: &mut [u8])
|
|
||||||
-> Result<usize> {
|
|
||||||
#[cfg(feature = "trace")] {
|
|
||||||
println!("framed::decode: Encoded = {:?}", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.len() == 0 {
|
/// Encode the supplied payload data as a frame and return it on the heap.
|
||||||
return Err(Error::EofBeforeFrame);
|
#[cfg(feature = "use_std")]
|
||||||
|
pub fn encode_to_box(&mut self, p: &Payload) -> Result<BoxEncoded> {
|
||||||
|
let mut buf = vec![0; max_encoded_len(p.len())];
|
||||||
|
let len = self.encode_to_slice(p, &mut *buf)?;
|
||||||
|
buf.truncate(len);
|
||||||
|
Ok(BoxEncoded::from(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.len() < FRAMING_LEN {
|
/// Encode the supplied payload data as a frame and write it to the
|
||||||
return Err(Error::EofDuringFrame);
|
/// supplied `Write`.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
#[cfg(feature = "use_std")]
|
||||||
|
pub fn encode_to_writer<W: Write>(&mut self, p: &Payload, w: &mut W)
|
||||||
|
-> Result<usize> {
|
||||||
|
let b = self.encode_to_box(p)?;
|
||||||
|
w.write_all(&*b.0)?;
|
||||||
|
Ok(b.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
if e[e.len()-1] != FRAME_END_SYMBOL {
|
/// Decode the supplied encoded frame, placing the payload at the
|
||||||
return Err(Error::EofDuringFrame)
|
/// beginning of the supplied buffer `dest`. Available from `no_std` crates.
|
||||||
}
|
///
|
||||||
|
/// When reading from a stream, the caller can continue reading data
|
||||||
assert!(dest.len() >= max_decoded_len(e.len()));
|
/// and buffering it until a `FRAME_END_SYMBOL` is read, then pass the
|
||||||
assert_eq!(e[e.len() - 1], FRAME_END_SYMBOL);
|
/// whole buffer including `FRAME_END_SYMBOL` to this function for
|
||||||
|
/// decoding.
|
||||||
let _header = &e[0..HEADER_LEN];
|
///
|
||||||
let body = &e[HEADER_LEN..(e.len() - FOOTER_LEN)];
|
/// If there is more than 1 FRAME_END_SYMBOL within `e`, the result
|
||||||
let footer = &e[(e.len() - FOOTER_LEN)..e.len()];
|
/// is undefined. Make sure you only pass 1 frame at a time.
|
||||||
|
///
|
||||||
#[cfg(feature = "trace")] {
|
/// Returns the length of the payload it has decoded.
|
||||||
println!("framed::decode: header = {:?}\n\
|
///
|
||||||
framed::decode: body = {:?}\n\
|
/// # Errors
|
||||||
framed::decode: footer = {:?}",
|
///
|
||||||
_header, body, footer);
|
/// Returns `Err(Error::EofDuringFrame`) if `e` contains >= 1 bytes of
|
||||||
}
|
/// a frame, but not a complete frame. A complete frame should have
|
||||||
|
/// `FRAME_END_SYMBOL` as the last byte.
|
||||||
let decoded_len = cobs::decode(body, dest)
|
///
|
||||||
.map_err(|_| Error::CobsDecodeFailed)?;
|
/// Returns `Err(Error::EofBeforeFrame`) if `e.len()` is 0.
|
||||||
|
///
|
||||||
let decoded = &dest[0..decoded_len];
|
/// # Panics
|
||||||
|
///
|
||||||
#[cfg(feature = "trace")] {
|
/// This function will panic if `dest` is not large enough for the decoded frame.
|
||||||
println!("framed::decode: payload = {:?}",
|
/// Ensure `dest.len() >= e.len()`.
|
||||||
decoded);
|
pub fn decode_to_slice(&mut self, e: &Encoded, dest: &mut [u8])
|
||||||
}
|
-> Result<usize> {
|
||||||
|
|
||||||
let calc_checksum = checksum(decoded);
|
|
||||||
let received_checksum = &footer[0..CHECKSUM_LEN];
|
|
||||||
|
|
||||||
#[cfg(feature = "trace")] {
|
|
||||||
println!("framed::decode: calc checksum = {:?}\n\
|
|
||||||
framed::decode: recv checksum = {:?}",
|
|
||||||
calc_checksum, received_checksum);
|
|
||||||
}
|
|
||||||
|
|
||||||
if calc_checksum != received_checksum {
|
|
||||||
return Err(Error::ChecksumError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(decoded_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decode the supplied encoded frame, returning the payload on the heap.
|
|
||||||
#[cfg(feature = "use_std")]
|
|
||||||
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 len = decode_to_slice(e, &mut buf)?;
|
|
||||||
buf.truncate(len);
|
|
||||||
Ok(BoxPayload::from(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads bytes from the supplied `Read` until it has a complete
|
|
||||||
/// encoded frame, then decodes the frame, returning the payload on the heap.
|
|
||||||
#[cfg(feature = "use_std")]
|
|
||||||
pub fn decode_from_reader<R: Read>(r: &mut Read) -> Result<BoxPayload> {
|
|
||||||
// Read until FRAME_END_SYMBOL
|
|
||||||
let mut next_frame = Vec::new();
|
|
||||||
let mut b = 0u8;
|
|
||||||
loop {
|
|
||||||
let res = r.read(ref_slice_mut(&mut b));
|
|
||||||
#[cfg(feature = "trace")] {
|
#[cfg(feature = "trace")] {
|
||||||
println!("framed: Read result = {:?}", res);
|
println!("framed::decode: Encoded = {:?}", e);
|
||||||
}
|
}
|
||||||
match res {
|
|
||||||
// In the 2 EOF cases defer to decode_to_box to return the
|
|
||||||
// correct error (EofBeforeFrame or EofDuringFrame).
|
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof =>
|
|
||||||
return decode_to_box(&*next_frame),
|
|
||||||
Ok(0) =>
|
|
||||||
return decode_to_box(&*next_frame),
|
|
||||||
|
|
||||||
Err(e) => return Err(Error::from(e)),
|
if e.len() == 0 {
|
||||||
Ok(1) => (),
|
return Err(Error::EofBeforeFrame);
|
||||||
Ok(_) => unreachable!(),
|
}
|
||||||
};
|
|
||||||
|
if e.len() < self.framing_len() {
|
||||||
|
return Err(Error::EofDuringFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if e[e.len()-1] != FRAME_END_SYMBOL {
|
||||||
|
return Err(Error::EofDuringFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(dest.len() >= max_decoded_len(e.len()));
|
||||||
|
assert_eq!(e[e.len() - 1], FRAME_END_SYMBOL);
|
||||||
|
|
||||||
|
let _header = &e[0..self.header_len()];
|
||||||
|
let body = &e[self.header_len()..(e.len() - self.footer_len())];
|
||||||
|
let footer = &e[(e.len() - self.footer_len())..e.len()];
|
||||||
|
|
||||||
#[cfg(feature = "trace")] {
|
#[cfg(feature = "trace")] {
|
||||||
println!("framed: Read byte = {}", b);
|
println!("framed::decode: header = {:?}\n\
|
||||||
|
framed::decode: body = {:?}\n\
|
||||||
|
framed::decode: footer = {:?}",
|
||||||
|
_header, body, footer);
|
||||||
}
|
}
|
||||||
next_frame.push(b);
|
|
||||||
if b == FRAME_END_SYMBOL {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert_eq!(next_frame[next_frame.len()-1], FRAME_END_SYMBOL);
|
|
||||||
|
|
||||||
decode_to_box(&*next_frame)
|
let decoded_len = cobs::decode(body, dest)
|
||||||
}
|
.map_err(|_| Error::CobsDecodeFailed)?;
|
||||||
|
|
||||||
|
let decoded = &dest[0..decoded_len];
|
||||||
|
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed::decode: payload = {:?}",
|
||||||
|
decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
let checksum = self.checksum();
|
||||||
|
let calc_checksum = checksum.calculate(decoded);
|
||||||
|
let received_checksum = &footer[0..checksum.len()];
|
||||||
|
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed::decode: calc checksum = {:?}\n\
|
||||||
|
framed::decode: recv checksum = {:?}",
|
||||||
|
calc_checksum, received_checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if &*calc_checksum != received_checksum {
|
||||||
|
return Err(Error::ChecksumError);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(decoded_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode the supplied encoded frame, returning the payload on the heap.
|
||||||
|
#[cfg(feature = "use_std")]
|
||||||
|
pub fn decode_to_box(&mut self, e: &Encoded) -> Result<BoxPayload> {
|
||||||
|
if e.len() == 0 {
|
||||||
|
return Err(Error::EofBeforeFrame);
|
||||||
|
}
|
||||||
|
let mut buf = vec![0; max_decoded_len(e.len())];
|
||||||
|
let len = self.decode_to_slice(e, &mut buf)?;
|
||||||
|
buf.truncate(len);
|
||||||
|
Ok(BoxPayload::from(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads bytes from the supplied `Read` until it has a complete
|
||||||
|
/// encoded frame, then decodes the frame, returning the payload on the heap.
|
||||||
|
#[cfg(feature = "use_std")]
|
||||||
|
pub fn decode_from_reader<R: Read>(&mut self, r: &mut Read)
|
||||||
|
-> Result<BoxPayload> {
|
||||||
|
// Read until FRAME_END_SYMBOL
|
||||||
|
let mut next_frame = Vec::new();
|
||||||
|
let mut b = 0u8;
|
||||||
|
loop {
|
||||||
|
let res = r.read(ref_slice_mut(&mut b));
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed: Read result = {:?}", res);
|
||||||
|
}
|
||||||
|
match res {
|
||||||
|
// In the 2 EOF cases defer to decode_to_box to return the
|
||||||
|
// correct error (EofBeforeFrame or EofDuringFrame).
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof =>
|
||||||
|
return self.decode_to_box(&*next_frame),
|
||||||
|
Ok(0) =>
|
||||||
|
return self.decode_to_box(&*next_frame),
|
||||||
|
|
||||||
|
Err(e) => return Err(Error::from(e)),
|
||||||
|
Ok(1) => (),
|
||||||
|
Ok(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed: Read byte = {}", b);
|
||||||
|
}
|
||||||
|
next_frame.push(b);
|
||||||
|
if b == FRAME_END_SYMBOL {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(next_frame[next_frame.len()-1], FRAME_END_SYMBOL);
|
||||||
|
|
||||||
|
self.decode_to_box(&*next_frame)
|
||||||
|
}
|
||||||
|
} // End of impl Codec.
|
||||||
|
|
||||||
const_fn! {
|
const_fn! {
|
||||||
/// Returns an upper bound for the decoded length of the payload
|
/// Returns an upper bound for the decoded length of the payload
|
||||||
|
@ -308,7 +395,7 @@ const_fn! {
|
||||||
/// Useful for calculating an appropriate buffer length.
|
/// Useful for calculating an appropriate buffer length.
|
||||||
pub fn max_decoded_len(code_len: usize) -> usize {
|
pub fn max_decoded_len(code_len: usize) -> usize {
|
||||||
// This is an over-estimate of the required decoded buffer, but
|
// This is an over-estimate of the required decoded buffer, but
|
||||||
// wasting HEADER_LEN + FOOTER_LEN bytes should be acceptable and
|
// wasting MAX_HEADER_LEN + MAX_FOOTER_LEN bytes should be acceptable and
|
||||||
// we can calculate this trivially in a const fn.
|
// we can calculate this trivially in a const fn.
|
||||||
code_len
|
code_len
|
||||||
}
|
}
|
||||||
|
@ -320,9 +407,8 @@ const_fn! {
|
||||||
///
|
///
|
||||||
/// Useful for calculating an appropriate buffer length.
|
/// Useful for calculating an appropriate buffer length.
|
||||||
pub fn max_encoded_len(payload_len: usize) -> usize {
|
pub fn max_encoded_len(payload_len: usize) -> usize {
|
||||||
HEADER_LEN
|
MAX_FRAMING_LEN
|
||||||
+ cobs_max_encoded_len(payload_len)
|
+ cobs_max_encoded_len(payload_len)
|
||||||
+ FOOTER_LEN
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,20 +434,16 @@ const_fn! {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends encoded frames over an inner `std::io::Write` instance.
|
/// Sends encoded frames over an inner `std::io::Write` instance.
|
||||||
|
///
|
||||||
|
/// Construct an instance using the method `Config::to_sender`
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
pub struct Sender<W: Write> {
|
pub struct Sender<W: Write> {
|
||||||
|
codec: Codec,
|
||||||
w: W,
|
w: W,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
impl<W: Write> Sender<W> {
|
impl<W: Write> Sender<W> {
|
||||||
/// 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 `std::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
|
||||||
|
@ -381,7 +463,7 @@ impl<W: Write> Sender<W> {
|
||||||
///
|
///
|
||||||
/// See also: [`send`](#method.send)
|
/// See also: [`send`](#method.send)
|
||||||
pub fn queue(&mut self, p: &Payload) -> Result<usize> {
|
pub fn queue(&mut self, p: &Payload) -> Result<usize> {
|
||||||
encode_to_writer(p, &mut self.w)
|
self.codec.encode_to_writer(p, &mut self.w)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the supplied payload as a frame, write it to the
|
/// Encode the supplied payload as a frame, write it to the
|
||||||
|
@ -399,21 +481,16 @@ impl<W: Write> Sender<W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receives encoded frames from an inner `std::io::Read` instance.
|
/// Receives encoded frames from an inner `std::io::Read` instance.
|
||||||
|
///
|
||||||
|
/// Construct an instance using the method `Config::to_receiver`
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
pub struct Receiver<R: Read> {
|
pub struct Receiver<R: Read> {
|
||||||
|
codec: Codec,
|
||||||
r: R,
|
r: R,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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
|
|
||||||
/// `std::io::Read`.
|
|
||||||
pub fn new(r: R) -> Receiver<R> {
|
|
||||||
Receiver::<R> {
|
|
||||||
r: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume this `Receiver` and return the inner `io::Read`.
|
/// Consume this `Receiver` and return the inner `io::Read`.
|
||||||
pub fn into_inner(self) -> R {
|
pub fn into_inner(self) -> R {
|
||||||
self.r
|
self.r
|
||||||
|
@ -422,7 +499,7 @@ impl<R: Read> Receiver<R> {
|
||||||
/// Receive an encoded frame from the inner `io::Read`, decode it
|
/// Receive an encoded frame from the inner `io::Read`, decode it
|
||||||
/// and return the payload.
|
/// and return the payload.
|
||||||
pub fn recv(&mut self) -> Result<BoxPayload> {
|
pub fn recv(&mut self) -> Result<BoxPayload> {
|
||||||
decode_from_reader::<R>(&mut self.r)
|
self.codec.decode_from_reader::<R>(&mut self.r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,6 +527,10 @@ mod tests {
|
||||||
assert_eq!(max_decoded_len(255), 255);
|
assert_eq!(max_decoded_len(255), 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn codec() -> Codec {
|
||||||
|
Config::default().into_codec()
|
||||||
|
}
|
||||||
|
|
||||||
// A test payload.
|
// A test payload.
|
||||||
const PAYLOAD: [u8; PAYLOAD_LEN] = [0, 1, 2, 3];
|
const PAYLOAD: [u8; PAYLOAD_LEN] = [0, 1, 2, 3];
|
||||||
const PAYLOAD_LEN: usize = 4;
|
const PAYLOAD_LEN: usize = 4;
|
||||||
|
@ -458,7 +539,7 @@ mod tests {
|
||||||
|
|
||||||
/// Returns an encoded frame with payload PAYLOAD.
|
/// Returns an encoded frame with payload PAYLOAD.
|
||||||
fn encoded_payload(buf: &mut [u8; ENCODED_LEN]) -> &[u8] {
|
fn encoded_payload(buf: &mut [u8; ENCODED_LEN]) -> &[u8] {
|
||||||
let len = encode_to_slice(&PAYLOAD, buf).unwrap();
|
let len = codec().encode_to_slice(&PAYLOAD, buf).unwrap();
|
||||||
&buf[0..len]
|
&buf[0..len]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +551,7 @@ mod tests {
|
||||||
encoded, payload);
|
encoded, payload);
|
||||||
}
|
}
|
||||||
let mut decoded_buf = [0; 100];
|
let mut decoded_buf = [0; 100];
|
||||||
let len = decode_to_slice(encoded, &mut decoded_buf).unwrap();
|
let len = codec().decode_to_slice(encoded, &mut decoded_buf).unwrap();
|
||||||
let decoded = &decoded_buf[0..len];
|
let decoded = &decoded_buf[0..len];
|
||||||
assert_eq!(decoded, payload);
|
assert_eq!(decoded, payload);
|
||||||
}
|
}
|
||||||
|
@ -479,14 +560,14 @@ mod tests {
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn encode_to_slice_dest_too_small() {
|
fn encode_to_slice_dest_too_small() {
|
||||||
let mut encoded_buf = [0u8; PAYLOAD_LEN];;
|
let mut encoded_buf = [0u8; PAYLOAD_LEN];;
|
||||||
let _ = encode_to_slice(&PAYLOAD, &mut encoded_buf);
|
let _ = codec().encode_to_slice(&PAYLOAD, &mut encoded_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn encode_to_slice_ok_dynamic_dest() {
|
fn encode_to_slice_ok_dynamic_dest() {
|
||||||
let mut encoded_buf = vec![0u8; max_encoded_len(PAYLOAD.len())];
|
let mut encoded_buf = vec![0u8; max_encoded_len(PAYLOAD.len())];
|
||||||
let len = encode_to_slice(&PAYLOAD, &mut *encoded_buf).unwrap();
|
let len = codec().encode_to_slice(&PAYLOAD, &mut *encoded_buf).unwrap();
|
||||||
let encoded = &encoded_buf[0..len];
|
let encoded = &encoded_buf[0..len];
|
||||||
|
|
||||||
assert_payload_eq(encoded, &PAYLOAD);
|
assert_payload_eq(encoded, &PAYLOAD);
|
||||||
|
@ -497,7 +578,7 @@ mod tests {
|
||||||
#[cfg(feature = "use_nightly")]
|
#[cfg(feature = "use_nightly")]
|
||||||
fn encode_to_slice_ok_static_dest() {
|
fn encode_to_slice_ok_static_dest() {
|
||||||
let mut encoded_buf = [0u8; max_encoded_len(PAYLOAD_LEN)];
|
let mut encoded_buf = [0u8; max_encoded_len(PAYLOAD_LEN)];
|
||||||
let len = encode_to_slice(&PAYLOAD, &mut encoded_buf).unwrap();
|
let len = codec().encode_to_slice(&PAYLOAD, &mut encoded_buf).unwrap();
|
||||||
let encoded = &encoded_buf[0..len];
|
let encoded = &encoded_buf[0..len];
|
||||||
|
|
||||||
assert_payload_eq(encoded, &PAYLOAD);
|
assert_payload_eq(encoded, &PAYLOAD);
|
||||||
|
@ -507,7 +588,7 @@ mod tests {
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn encode_to_writer_ok() {
|
fn encode_to_writer_ok() {
|
||||||
let mut encoded = vec![];
|
let mut encoded = vec![];
|
||||||
encode_to_writer(&PAYLOAD, &mut encoded).unwrap();
|
codec().encode_to_writer(&PAYLOAD, &mut encoded).unwrap();
|
||||||
assert_payload_eq(&*encoded, &PAYLOAD);
|
assert_payload_eq(&*encoded, &PAYLOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,15 +598,15 @@ mod tests {
|
||||||
let mut buf = [0; ENCODED_LEN];
|
let mut buf = [0; ENCODED_LEN];
|
||||||
let encoded = encoded_payload(&mut buf);
|
let encoded = encoded_payload(&mut buf);
|
||||||
let mut decoded_buf = [0u8; PAYLOAD_LEN - 1];
|
let mut decoded_buf = [0u8; PAYLOAD_LEN - 1];
|
||||||
let _ = decode_to_slice(&*encoded, &mut decoded_buf);
|
let _ = codec().decode_to_slice(&*encoded, &mut decoded_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn decode_to_slice_ok_dynamic_dest() {
|
fn decode_to_slice_ok_dynamic_dest() {
|
||||||
let encoded = encode_to_box(&PAYLOAD).unwrap();
|
let encoded = codec().encode_to_box(&PAYLOAD).unwrap();
|
||||||
let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())];
|
let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())];
|
||||||
let len = decode_to_slice(&*encoded, &mut decoded_buf).unwrap();
|
let len = codec().decode_to_slice(&*encoded, &mut decoded_buf).unwrap();
|
||||||
let decoded = &decoded_buf[0..len];
|
let decoded = &decoded_buf[0..len];
|
||||||
|
|
||||||
assert_eq!(&PAYLOAD, decoded);
|
assert_eq!(&PAYLOAD, decoded);
|
||||||
|
@ -536,7 +617,7 @@ mod tests {
|
||||||
fn decode_to_slice_no_end_symbol() {
|
fn decode_to_slice_no_end_symbol() {
|
||||||
let encoded = vec![FRAME_END_SYMBOL + 1; max_encoded_len(0)];
|
let encoded = vec![FRAME_END_SYMBOL + 1; max_encoded_len(0)];
|
||||||
let mut decoded_buf = [];
|
let mut decoded_buf = [];
|
||||||
let res = decode_to_slice(&*encoded, &mut decoded_buf);
|
let res = codec().decode_to_slice(&*encoded, &mut decoded_buf);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(Error::EofDuringFrame) => (),
|
Err(Error::EofDuringFrame) => (),
|
||||||
|
@ -547,9 +628,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn decode_to_slice_encoded_too_short() {
|
fn decode_to_slice_encoded_too_short() {
|
||||||
let encoded = vec![FRAME_END_SYMBOL; FRAMING_LEN - 1];
|
let mut c = codec();
|
||||||
|
let encoded = vec![FRAME_END_SYMBOL; c.framing_len() - 1];
|
||||||
let mut decoded_buf = [];
|
let mut decoded_buf = [];
|
||||||
let res = decode_to_slice(&*encoded, &mut decoded_buf);
|
let res = c.decode_to_slice(&*encoded, &mut decoded_buf);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(Error::EofDuringFrame) => (),
|
Err(Error::EofDuringFrame) => (),
|
||||||
|
@ -562,7 +644,7 @@ mod tests {
|
||||||
fn decode_to_slice_encoded_empty() {
|
fn decode_to_slice_encoded_empty() {
|
||||||
let encoded = vec![];
|
let encoded = vec![];
|
||||||
let mut decoded_buf = [];
|
let mut decoded_buf = [];
|
||||||
let res = decode_to_slice(&*encoded, &mut decoded_buf);
|
let res = codec().decode_to_slice(&*encoded, &mut decoded_buf);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(Error::EofBeforeFrame) => (),
|
Err(Error::EofBeforeFrame) => (),
|
||||||
|
@ -573,19 +655,21 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn decode_to_slice_bad_checksum() {
|
fn decode_to_slice_bad_checksum() {
|
||||||
let encoded = encode_to_box(&PAYLOAD).unwrap();
|
let mut c = codec();
|
||||||
|
let encoded = c.encode_to_box(&PAYLOAD).unwrap();
|
||||||
let mut encoded = Vec::from(&*encoded);
|
let mut encoded = Vec::from(&*encoded);
|
||||||
let checksum_offset = encoded.len() - FOOTER_LEN;
|
let checksum_offset = encoded.len() - c.footer_len();
|
||||||
|
|
||||||
{
|
{
|
||||||
let checksum = &mut encoded[checksum_offset..
|
let checksum =
|
||||||
(checksum_offset + CHECKSUM_LEN)];
|
&mut encoded[checksum_offset..
|
||||||
|
(checksum_offset + c.config.checksum.len())];
|
||||||
checksum[0] = checksum[0].wrapping_add(1);
|
checksum[0] = checksum[0].wrapping_add(1);
|
||||||
checksum[1] = checksum[1].wrapping_add(2);
|
checksum[1] = checksum[1].wrapping_add(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())];
|
let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())];
|
||||||
let res = decode_to_slice(&*encoded, &mut decoded_buf);
|
let res = codec().decode_to_slice(&*encoded, &mut decoded_buf);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(Error::ChecksumError) => (),
|
Err(Error::ChecksumError) => (),
|
||||||
|
@ -596,11 +680,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn decode_to_slice_missing_bytes() {
|
fn decode_to_slice_missing_bytes() {
|
||||||
let encoded = encode_to_box(&PAYLOAD).unwrap();
|
let encoded = codec().encode_to_box(&PAYLOAD).unwrap();
|
||||||
let encoded = &encoded[1..encoded.len()];
|
let encoded = &encoded[1..encoded.len()];
|
||||||
|
|
||||||
let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())];
|
let mut decoded_buf = vec![0u8; max_decoded_len(encoded.len())];
|
||||||
let res = decode_to_slice(&*encoded, &mut decoded_buf);
|
let res = codec().decode_to_slice(&*encoded, &mut decoded_buf);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(Error::ChecksumError) => (),
|
Err(Error::ChecksumError) => (),
|
||||||
|
@ -611,8 +695,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn decode_to_box_ok() {
|
fn decode_to_box_ok() {
|
||||||
let encoded = encode_to_box(&PAYLOAD).unwrap();
|
let encoded = codec().encode_to_box(&PAYLOAD).unwrap();
|
||||||
let decoded = decode_to_box(&*encoded).unwrap();
|
let decoded = codec().decode_to_box(&*encoded).unwrap();
|
||||||
|
|
||||||
assert_eq!(&PAYLOAD, &*decoded);
|
assert_eq!(&PAYLOAD, &*decoded);
|
||||||
}
|
}
|
||||||
|
@ -620,9 +704,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
fn decode_from_reader_ok() {
|
fn decode_from_reader_ok() {
|
||||||
let encoded = encode_to_box(&PAYLOAD).unwrap();
|
let mut c = codec();
|
||||||
|
let encoded = c.encode_to_box(&PAYLOAD).unwrap();
|
||||||
let mut reader = Cursor::new(&*encoded);
|
let mut reader = Cursor::new(&*encoded);
|
||||||
let decoded = decode_from_reader::<Cursor<&[u8]>>(&mut reader).unwrap();
|
let decoded = c.decode_from_reader::<Cursor<&[u8]>>(&mut reader).unwrap();
|
||||||
assert_eq!(&*decoded, &PAYLOAD);
|
assert_eq!(&*decoded, &PAYLOAD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -692,9 +777,9 @@ mod rw_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn partial_input() {
|
fn partial_input() {
|
||||||
let c = Channel::new();
|
let chan = Channel::new();
|
||||||
let mut rx = Receiver::new(Box::new(c.reader()) as Box<Read>);
|
let mut rx = config().into_receiver(chan.reader());
|
||||||
let mut tx_raw = c.writer();
|
let mut tx_raw = chan.writer();
|
||||||
tx_raw.write(&[0x01]).unwrap();
|
tx_raw.write(&[0x01]).unwrap();
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Err(Error::EofDuringFrame) => (),
|
Err(Error::EofDuringFrame) => (),
|
||||||
|
@ -702,10 +787,15 @@ mod rw_tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn config() -> Config {
|
||||||
|
Config::default()
|
||||||
|
}
|
||||||
|
|
||||||
fn pair() -> (Sender<Box<Write>>, Receiver<Box<Read>>) {
|
fn pair() -> (Sender<Box<Write>>, Receiver<Box<Read>>) {
|
||||||
let c = Channel::new();
|
let chan = Channel::new();
|
||||||
let tx = Sender::new(Box::new(c.writer()) as Box<Write>);
|
let c = config();
|
||||||
let rx = Receiver::new(Box::new(c.reader()) as Box<Read>);
|
let tx = c.clone().into_sender(Box::new(chan.writer()) as Box<Write>);
|
||||||
|
let rx = c.clone().into_receiver(Box::new(chan.reader()) as Box<Read>);
|
||||||
(tx, rx)
|
(tx, rx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
//! 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};
|
||||||
|
|
78
framed/src/checksum.rs
Normal file
78
framed/src/checksum.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use ::Payload;
|
||||||
|
use byteorder::{self, ByteOrder};
|
||||||
|
use core::cmp::PartialEq;
|
||||||
|
use core::fmt::{self, Debug, Formatter};
|
||||||
|
use core::ops::Deref;
|
||||||
|
use crc16;
|
||||||
|
|
||||||
|
/// A checksum algorithm configuration to use when encoding data.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Checksum {
|
||||||
|
/// Use no checksum.
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// CRC-16/CDMA2000 as [implemented in crate `crc16`][impl].
|
||||||
|
/// This is the default checksum.
|
||||||
|
///
|
||||||
|
/// [impl]: https://docs.rs/crc16/0.3.4/crc16/enum.CDMA2000.html
|
||||||
|
Crc16Cdma2000,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Checksum {
|
||||||
|
fn default() -> Checksum {
|
||||||
|
Checksum::Crc16Cdma2000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const MAX_CHECKSUM_LEN: usize = 2;
|
||||||
|
|
||||||
|
#[derive(Eq)]
|
||||||
|
pub(crate) struct ChecksumValue {
|
||||||
|
data: [u8; MAX_CHECKSUM_LEN],
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Checksum {
|
||||||
|
pub(crate) fn len(&self) -> usize {
|
||||||
|
match *self {
|
||||||
|
Checksum::None => 0,
|
||||||
|
Checksum::Crc16Cdma2000 => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn calculate(&self, payload: &Payload) -> ChecksumValue {
|
||||||
|
let mut v = ChecksumValue {
|
||||||
|
data: [0u8; MAX_CHECKSUM_LEN],
|
||||||
|
len: self.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Checksum::None => (),
|
||||||
|
Checksum::Crc16Cdma2000 => {
|
||||||
|
let u16 = crc16::State::<crc16::CDMA2000>::calculate(payload);
|
||||||
|
byteorder::NetworkEndian::write_u16(&mut v.data, u16);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ChecksumValue {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
&self.data[0..self.len]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ChecksumValue {
|
||||||
|
fn eq(&self, other: &ChecksumValue) -> bool {
|
||||||
|
self.deref() == other.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ChecksumValue {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "{:?}", self.deref())
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@
|
||||||
//! * Nothing here yet.
|
//! * Nothing here yet.
|
||||||
//! * Body: payload [COBS]-encoded to remove bytes equal to zero
|
//! * Body: payload [COBS]-encoded to remove bytes equal to zero
|
||||||
//! * Footer:
|
//! * Footer:
|
||||||
//! * A 2 byte checksum.
|
//! * A variable length checksum, depending on configuration in `framed::bytes::Config`.
|
||||||
//! * A terminating zero byte.
|
//! * A terminating zero byte.
|
||||||
//!
|
//!
|
||||||
//! [COBS]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
|
//! [COBS]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
|
||||||
|
@ -83,6 +83,7 @@
|
||||||
//! the `use_std` Cargo feature.
|
//! the `use_std` Cargo feature.
|
||||||
|
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
|
#![deny(missing_docs)]
|
||||||
#![cfg_attr(not(feature = "use_std"), no_std)]
|
#![cfg_attr(not(feature = "use_std"), no_std)]
|
||||||
|
|
||||||
#![cfg_attr(feature = "use_nightly", feature(const_fn))]
|
#![cfg_attr(feature = "use_nightly", feature(const_fn))]
|
||||||
|
@ -196,8 +197,10 @@ extern crate ssmarshal;
|
||||||
// ## Sub-modules
|
// ## Sub-modules
|
||||||
pub mod bytes;
|
pub mod bytes;
|
||||||
|
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(all(test, feature = "use_std"))]
|
||||||
pub mod channel;
|
mod channel;
|
||||||
|
|
||||||
|
mod checksum;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
|
|
@ -34,16 +34,17 @@
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! let payload = Test { a: 1, b: 2 };
|
//! let payload = Test { a: 1, b: 2 };
|
||||||
|
//! let config = framed::bytes::Config::default().typed::<Test>();
|
||||||
//!
|
//!
|
||||||
//! let mut encoded = vec![];
|
//! let mut encoded = vec![];
|
||||||
//! {
|
//! {
|
||||||
//! let mut sender = Sender::<_, Test>::new(&mut encoded);
|
//! let mut sender = config.clone().into_sender(&mut encoded);
|
||||||
//! sender.send(&payload).expect("send ok");
|
//! sender.send(&payload).expect("send ok");
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! // `encoded` now contains the encoded value.
|
//! // `encoded` now contains the encoded value.
|
||||||
//!
|
//!
|
||||||
//! let mut receiver = Receiver::<_, Test>::new(Cursor::new(encoded));
|
//! let mut receiver = config.clone().into_receiver(Cursor::new(encoded));
|
||||||
//! let decoded = receiver.recv().expect("recv ok");
|
//! let decoded = receiver.recv().expect("recv ok");
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(payload, decoded);
|
//! assert_eq!(payload, decoded);
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! # extern crate framed;
|
//! # extern crate framed;
|
||||||
|
//! # use framed::bytes;
|
||||||
//! # use framed::typed::*;
|
//! # use framed::typed::*;
|
||||||
//! # extern crate serde;
|
//! # extern crate serde;
|
||||||
//! # #[macro_use]
|
//! # #[macro_use]
|
||||||
|
@ -71,10 +73,12 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! #
|
//! #
|
||||||
//! # let payload = Test { a: 1, b: 2 };
|
//! # let payload = Test { a: 1, b: 2 };
|
||||||
|
//! # let config = bytes::Config::default().typed::<Test>();
|
||||||
//! #
|
//! #
|
||||||
|
//! let mut codec = config.into_codec();
|
||||||
//! 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 = codec.encode_to_slice(
|
||||||
//! &payload,
|
//! &payload,
|
||||||
//! &mut ser_buf,
|
//! &mut ser_buf,
|
||||||
//! &mut encoded_buf
|
//! &mut encoded_buf
|
||||||
|
@ -84,8 +88,8 @@
|
||||||
//! // `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 decoded = decode_from_slice(encoded, &mut de_buf)
|
//! let decoded = codec.decode_from_slice(encoded, &mut de_buf)
|
||||||
//! .expect("decode ok");
|
//! .expect("decode ok");
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(payload, decoded);
|
//! assert_eq!(payload, decoded);
|
||||||
//! # }
|
//! # }
|
||||||
|
@ -94,7 +98,6 @@
|
||||||
use ::{Encoded, TempBuffer};
|
use ::{Encoded, TempBuffer};
|
||||||
use ::bytes;
|
use ::bytes;
|
||||||
use ::error::{Result};
|
use ::error::{Result};
|
||||||
#[cfg(feature = "use_std")]
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -103,64 +106,131 @@ use ssmarshal;
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
/// Serializes and encodes the supplied value `v` into destination
|
/// Contains methods for encoding and decoding a serializable type `T`
|
||||||
/// buffer `dest`, using `ser_buf` as a temporary serialization buffer.
|
/// with a specific configuration.
|
||||||
/// Available from `no_std` crates.
|
|
||||||
///
|
///
|
||||||
/// Returns the number of bytes written to the beginning of `dest`.
|
/// Construct an instance from a `Config` instance with the
|
||||||
///
|
/// `Config::into_codec` method.
|
||||||
/// ## Panics
|
pub struct Codec<T: DeserializeOwned + Serialize> {
|
||||||
///
|
bytes_codec: bytes::Codec,
|
||||||
/// This will panic if the supplied buffers are too small to serialize
|
_phantom: PhantomData<T>,
|
||||||
/// a value of `T`. Callers must ensure that:
|
|
||||||
///
|
|
||||||
/// * `ser_buf.len() >= max_serialize_buf_len::<T>()` and
|
|
||||||
/// * `dest.len() >= max_encoded_len::<T>()`.
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// See the [no_std usage example][ex] in the `typed` module documentation.
|
|
||||||
/// [ex]: index.html#example-usage-from-a-no_std-crate
|
|
||||||
pub fn encode_to_slice<T: DeserializeOwned + 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];
|
|
||||||
bytes::encode_to_slice(ser, dest)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes the supplied encoded frame `e`, then deserializes its
|
/// Configurable options for encoding and decoding a serializable type
|
||||||
/// payload as a value of type `T`, using `de_buf` as a temporary
|
/// `T` using a builder pattern.
|
||||||
/// deserialization buffer. Available from `no_std` crates.
|
|
||||||
///
|
///
|
||||||
/// Returns the deserialized value.
|
/// Construct an instance with the method `framed::bytes::Config::typed<T>()`.
|
||||||
///
|
pub struct Config<T: DeserializeOwned + Serialize> {
|
||||||
/// ## Panics
|
bytes_config: bytes::Config,
|
||||||
///
|
_phantom: PhantomData<T>,
|
||||||
/// This will panic if the supplied buffer is too small to deserialize
|
}
|
||||||
/// a value of `T`. Callers must ensure that:
|
|
||||||
///
|
|
||||||
/// * `de_buf.len() >= max_serialize_buf_len::<T>()`.
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// See the [no_std usage example][ex] in the `typed` module documentation.
|
|
||||||
/// [ex]: index.html#example-usage-from-a-no_std-crate
|
|
||||||
pub fn decode_from_slice<T: DeserializeOwned + Serialize>(
|
|
||||||
e: &Encoded,
|
|
||||||
de_buf: &mut TempBuffer
|
|
||||||
) -> Result<T> {
|
|
||||||
assert!(de_buf.len() >= max_serialize_buf_len::<T>());
|
|
||||||
|
|
||||||
let de_len = bytes::decode_to_slice(e, de_buf)?;
|
impl<T: DeserializeOwned + Serialize> Clone for Config<T> {
|
||||||
let payload = &de_buf[0..de_len];
|
fn clone(&self) -> Config<T> {
|
||||||
let (v, _len) = ssmarshal::deserialize(payload)?;
|
Config::<T> {
|
||||||
Ok(v)
|
bytes_config: self.bytes_config.clone(),
|
||||||
|
_phantom: PhantomData::<T>::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: DeserializeOwned + Serialize> Config<T> {
|
||||||
|
pub(crate) fn new(bytes_config: bytes::Config) -> Config<T> {
|
||||||
|
Config::<T> {
|
||||||
|
bytes_config: bytes_config,
|
||||||
|
_phantom: PhantomData::<T>::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a `Codec` instance with this configuration.
|
||||||
|
pub fn into_codec(self) -> Codec<T> {
|
||||||
|
Codec::<T> {
|
||||||
|
bytes_codec: self.bytes_config.into_codec(),
|
||||||
|
_phantom: PhantomData::<T>::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use_std")]
|
||||||
|
/// Construct a `Receiver` instance with this configuration.
|
||||||
|
pub fn into_receiver<R: Read>(self, r: R) -> Receiver<R, T> {
|
||||||
|
Receiver::<R, T> {
|
||||||
|
codec: self.into_codec(),
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use_std")]
|
||||||
|
/// Construct a `Sender` instance with this configuration.
|
||||||
|
pub fn into_sender<W: Write>(self, w: W) -> Sender<W, T> {
|
||||||
|
Sender::<W, T> {
|
||||||
|
codec: self.into_codec(),
|
||||||
|
w: w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: DeserializeOwned + Serialize> Codec<T> {
|
||||||
|
/// Serializes and encodes the supplied value `v` into destination
|
||||||
|
/// buffer `dest`, using `ser_buf` as a temporary serialization buffer.
|
||||||
|
/// Available from `no_std` crates.
|
||||||
|
///
|
||||||
|
/// Returns the number of bytes written to the beginning of `dest`.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
///
|
||||||
|
/// This will panic if the supplied buffers are too small to serialize
|
||||||
|
/// a value of `T`. Callers must ensure that:
|
||||||
|
///
|
||||||
|
/// * `ser_buf.len() >= max_serialize_buf_len::<T>()` and
|
||||||
|
/// * `dest.len() >= max_encoded_len::<T>()`.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// See the [no_std usage example][ex] in the `typed` module documentation.
|
||||||
|
/// [ex]: index.html#example-usage-from-a-no_std-crate
|
||||||
|
pub fn encode_to_slice(
|
||||||
|
&mut self,
|
||||||
|
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];
|
||||||
|
self.bytes_codec.encode_to_slice(ser, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes the supplied encoded frame `e`, then deserializes its
|
||||||
|
/// payload as a value of type `T`, using `de_buf` as a temporary
|
||||||
|
/// deserialization buffer. Available from `no_std` crates.
|
||||||
|
///
|
||||||
|
/// Returns the deserialized value.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
///
|
||||||
|
/// This will panic if the supplied buffer is too small to deserialize
|
||||||
|
/// a value of `T`. Callers must ensure that:
|
||||||
|
///
|
||||||
|
/// * `de_buf.len() >= max_serialize_buf_len::<T>()`.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// See the [no_std usage example][ex] in the `typed` module documentation.
|
||||||
|
/// [ex]: index.html#example-usage-from-a-no_std-crate
|
||||||
|
pub fn decode_from_slice(
|
||||||
|
&mut self,
|
||||||
|
e: &Encoded,
|
||||||
|
de_buf: &mut TempBuffer
|
||||||
|
) -> Result<T> {
|
||||||
|
assert!(de_buf.len() >= max_serialize_buf_len::<T>());
|
||||||
|
|
||||||
|
let de_len = self.bytes_codec.decode_to_slice(e, de_buf)?;
|
||||||
|
let payload = &de_buf[0..de_len];
|
||||||
|
let (v, _len) = ssmarshal::deserialize(payload)?;
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const_fn! {
|
const_fn! {
|
||||||
|
@ -185,27 +255,20 @@ const_fn! {
|
||||||
|
|
||||||
/// Sends encoded structs of type `T` over an inner `std::io::Write` instance.
|
/// Sends encoded structs of type `T` over an inner `std::io::Write` instance.
|
||||||
///
|
///
|
||||||
|
/// Construct an instance using the method `Config::to_sender`
|
||||||
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// See the [std usage example][ex] in the `typed` module documentation.
|
/// See the [std usage example][ex] in the `typed` module documentation.
|
||||||
/// [ex]: index.html#example-usage-from-a-std-crate
|
/// [ex]: index.html#example-usage-from-a-std-crate
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
pub struct Sender<W: Write, T: Serialize> {
|
pub struct Sender<W: Write, T: DeserializeOwned + Serialize> {
|
||||||
|
codec: Codec<T>,
|
||||||
w: W,
|
w: W,
|
||||||
_t: PhantomData<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
impl<W: Write, T: Serialize> Sender<W, T> {
|
impl<W: Write, T: DeserializeOwned + Serialize> Sender<W, T> {
|
||||||
/// Construct a `Sender` that sends encoded structs over the supplied
|
|
||||||
/// `io::Write`.
|
|
||||||
pub fn new(w: W) -> Sender<W, T> {
|
|
||||||
Sender::<W, T> {
|
|
||||||
w: w,
|
|
||||||
_t: PhantomData::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume this `Sender` and return the inner `std::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
|
||||||
|
@ -239,11 +302,11 @@ impl<W: Write, T: Serialize> Sender<W, T> {
|
||||||
// I think this may require const generics
|
// I think this may require const generics
|
||||||
// (rust-lang tracking issue:
|
// (rust-lang tracking issue:
|
||||||
// https://github.com/rust-lang/rust/issues/44580).
|
// https://github.com/rust-lang/rust/issues/44580).
|
||||||
let mut ser_buf = vec![0u8; size_of::<T>()];
|
let mut ser_buf = vec![0u8; max_serialize_buf_len::<T>()];
|
||||||
|
|
||||||
let ser_len = ssmarshal::serialize(&mut ser_buf, v)?;
|
let ser_len = ssmarshal::serialize(&mut ser_buf, v)?;
|
||||||
let ser = &ser_buf[0..ser_len];
|
let ser = &ser_buf[0..ser_len];
|
||||||
bytes::encode_to_writer(&ser, &mut self.w)
|
self.codec.bytes_codec.encode_to_writer(&ser, &mut self.w)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode the supplied payload as a frame, write it to the
|
/// Encode the supplied payload as a frame, write it to the
|
||||||
|
@ -262,27 +325,20 @@ impl<W: Write, T: Serialize> Sender<W, T> {
|
||||||
|
|
||||||
/// Receives encoded structs of type `T` from an inner `std::io::Read` instance.
|
/// Receives encoded structs of type `T` from an inner `std::io::Read` instance.
|
||||||
///
|
///
|
||||||
|
/// Construct an instance using the method `Config::to_receiver`
|
||||||
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// See the [std usage example][ex] in the `typed` module documentation.
|
/// See the [std usage example][ex] in the `typed` module documentation.
|
||||||
/// [ex]: index.html#example-usage-from-a-std-crate
|
/// [ex]: index.html#example-usage-from-a-std-crate
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
pub struct Receiver<R: Read, T: DeserializeOwned> {
|
pub struct Receiver<R: Read, T: DeserializeOwned + Serialize> {
|
||||||
|
codec: Codec<T>,
|
||||||
r: R,
|
r: R,
|
||||||
_t: PhantomData<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
impl<R: Read, T: DeserializeOwned> Receiver<R, T> {
|
impl<R: Read, T: DeserializeOwned + Serialize> Receiver<R, T> {
|
||||||
/// Construct a `Receiver` that receives encoded structs from the supplied
|
|
||||||
/// `io::Read`.
|
|
||||||
pub fn new(r: R) -> Receiver<R, T> {
|
|
||||||
Receiver::<R, T> {
|
|
||||||
r: r,
|
|
||||||
_t: PhantomData::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume this `Receiver` and return the inner `std::io::Read`.
|
/// Consume this `Receiver` and return the inner `std::io::Read`.
|
||||||
pub fn into_inner(self) -> R {
|
pub fn into_inner(self) -> R {
|
||||||
self.r
|
self.r
|
||||||
|
@ -291,7 +347,8 @@ impl<R: Read, T: DeserializeOwned> Receiver<R, T> {
|
||||||
/// Receive an encoded frame from the inner `std::io::Read`, decode it
|
/// Receive an encoded frame from the inner `std::io::Read`, decode it
|
||||||
/// and return the payload.
|
/// and return the payload.
|
||||||
pub fn recv(&mut self) -> Result<T> {
|
pub fn recv(&mut self) -> Result<T> {
|
||||||
let payload = bytes::decode_from_reader::<R>(&mut self.r)?;
|
let payload =
|
||||||
|
self.codec.bytes_codec.decode_from_reader::<R>(&mut self.r)?;
|
||||||
let (v, _len) = ssmarshal::deserialize(&*payload)?;
|
let (v, _len) = ssmarshal::deserialize(&*payload)?;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
@ -342,6 +399,10 @@ mod tests {
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn codec() -> Codec<Test> {
|
||||||
|
bytes::Config::default().typed::<Test>().into_codec()
|
||||||
|
}
|
||||||
|
|
||||||
mod slice_tests {
|
mod slice_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -349,14 +410,15 @@ mod tests {
|
||||||
#[cfg(feature = "use_nightly")]
|
#[cfg(feature = "use_nightly")]
|
||||||
fn roundtrip() {
|
fn roundtrip() {
|
||||||
let input = test_val();
|
let input = test_val();
|
||||||
|
let mut c = codec();
|
||||||
let mut ser_buf = [0u8; max_serialize_buf_len::<Test>()];
|
let mut ser_buf = [0u8; max_serialize_buf_len::<Test>()];
|
||||||
let mut enc_buf = [0u8; max_encoded_len::<Test>()];
|
let mut enc_buf = [0u8; max_encoded_len::<Test>()];
|
||||||
let len = super::encode_to_slice(
|
let len = c.encode_to_slice(
|
||||||
&input, &mut ser_buf, &mut enc_buf
|
&input, &mut ser_buf, &mut enc_buf
|
||||||
).unwrap();
|
).unwrap();
|
||||||
let enc = &enc_buf[0..len];
|
let enc = &enc_buf[0..len];
|
||||||
|
|
||||||
let output = super::decode_from_slice(&enc, &mut ser_buf).unwrap();
|
let output = c.decode_from_slice(&enc, &mut ser_buf).unwrap();
|
||||||
assert_eq!(input, output);
|
assert_eq!(input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +427,7 @@ mod tests {
|
||||||
fn bad_ser_buf_len() {
|
fn bad_ser_buf_len() {
|
||||||
let mut ser_buf = [0u8; 1];
|
let mut ser_buf = [0u8; 1];
|
||||||
let mut enc = [0u8; 100];
|
let mut enc = [0u8; 100];
|
||||||
super::encode_to_slice(
|
codec().encode_to_slice(
|
||||||
&test_val(), &mut ser_buf, &mut enc
|
&test_val(), &mut ser_buf, &mut enc
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -375,7 +437,7 @@ mod tests {
|
||||||
fn bad_dest_len() {
|
fn bad_dest_len() {
|
||||||
let mut ser_buf = [0u8; 100];
|
let mut ser_buf = [0u8; 100];
|
||||||
let mut enc = [0u8; 1];
|
let mut enc = [0u8; 1];
|
||||||
super::encode_to_slice(
|
codec().encode_to_slice(
|
||||||
&test_val(), &mut ser_buf, &mut enc
|
&test_val(), &mut ser_buf, &mut enc
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -427,9 +489,12 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pair() -> (Sender<Box<Write>, Test>, Receiver<Box<Read>, Test>) {
|
fn pair() -> (Sender<Box<Write>, Test>, Receiver<Box<Read>, Test>) {
|
||||||
let c = Channel::new();
|
let chan = Channel::new();
|
||||||
let tx = Sender::new(Box::new(c.writer()) as Box<Write>);
|
let conf = bytes::Config::default().typed::<Test>();
|
||||||
let rx = Receiver::new(Box::new(c.reader()) as Box<Read>);
|
let tx = conf.clone()
|
||||||
|
.into_sender(Box::new(chan.writer()) as Box<Write>);
|
||||||
|
let rx = conf.clone()
|
||||||
|
.into_receiver(Box::new(chan.reader()) as Box<Read>);
|
||||||
(tx, rx)
|
(tx, rx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
extern crate framed;
|
extern crate framed;
|
||||||
extern crate framed_test_type as lib;
|
extern crate framed_test_type as lib;
|
||||||
|
|
||||||
use framed::typed::Sender;
|
|
||||||
use lib::Test;
|
use lib::Test;
|
||||||
use std::io::stdout;
|
use std::io::stdout;
|
||||||
|
|
||||||
|
@ -14,6 +13,8 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!("test_type/main.rs: Sending sample value: {:#?}", t);
|
eprintln!("test_type/main.rs: Sending sample value: {:#?}", t);
|
||||||
let mut s = Sender::<_, Test>::new(stdout());
|
let mut s = framed::bytes::Config::default()
|
||||||
|
.typed::<Test>()
|
||||||
|
.into_sender(stdout());
|
||||||
s.send(&t).unwrap();
|
s.send(&t).unwrap();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue