Crate async_codec[][src]

Expand description

Async Encode/Decode helpers

For prior art, see tokio-codec. This borrows many of the ideas that originated there, though with a simpler and no_std-compatible approach. No allocation is strictly required by implementers of the Encode or Decode traits, making them suitable for embedded applications.

Encoders

Encoders are types that implement the Encode trait. Consumers of this trait will provide an Item to be encoded, along with a buffer into which the resulting bytes should be placed. The Encoder may return one of three from this operation:

  • EncodeResult::Ok(n): The item was successfully encodeded into the provided buffer and took n bytes.
  • EncodeResult::Err(e): There was an unrecoverable error encoding the item. Subsequent calls with the same item should return the same error.
  • EncodeResult::Overflow(n): The item could be encoded, but the encoded representation would overflow the buffer. If n > 0, then a buffer of length n is required to hold the serialized item.

Upon an Overflow result, the caller should allocate a bigger buffer (if possible), and attempt the operation again. Encode implementations may keep a previously encoded Item in an internal buffer to make re-attempted encodings cheaper. If this approach is used, the Encode::reset method must also be implemented.

Decoders

Decoders implement the Decode trait. To decode an Item, callers provide the decoder with a buffer containing bytes from the stream. The decoder returns a usize indicating that it consumed some number of bytes from the buffer, along with one of three results:

  • DecodeResult::Ok(item): The item was successfully decoded
  • DecodeResult::Err(e): An error e arose when decoding the item. This does not necessarily invalidate the stream.
  • DecodeResult::UnexpectedEnd: Not enough data to decode yet, caller should read more and try again with more bytes.

Decoders are free to consume bytes in-line with returning UnexpectedEnd results and keep a running internal buffer of what’s been read so far, or they may opt for consuming the bytes for an Item all at once. They should consume bytes in the error case as well so that the caller doesn’t provide the same bad data twice.

Framed

The Framed type provides a wrapper for AsyncRead + AsyncWrite streams that applies an Encode + Decode object to create an async Stream + Sink of Items. It manages the reads and writes against the underlying stream and the buffers provided to the encode/decode operations. Its error types (ReadFrameError and WriteFrameError) differentiate between IO errors in the stream (which are usually fatal), and codec errors (which might not be).

Example

use std::{
    io,
    str::Utf8Error,
};

use futures::{
    io::Cursor,
    prelude::*,
};

use async_codec::*;

struct LineCodec;

impl Encode for LineCodec {
    type Item = String;
    type Error = ();
    fn encode(&mut self, item: &String, buf: &mut [u8]) -> EncodeResult<()> {
        let needed = item.as_bytes().len() + 1;
        if buf.len() < needed {
            return EncodeResult::Overflow(needed);
        }
        buf[..needed - 1].copy_from_slice(item.as_bytes());
        buf[needed - 1] = b'\n';
        Ok(needed).into()
    }
}

impl Decode for LineCodec {
    type Item = String;
    type Error = Utf8Error;

    fn decode(&mut self, buf: &mut [u8]) -> (usize, DecodeResult<String, Utf8Error>) {
        let newline = match buf.iter().position(|b| *b == b'\n') {
            Some(idx) => idx,
            None => return (0, DecodeResult::UnexpectedEnd),
        };
        let string_bytes = &buf[..newline];
        (
            newline + 1,
            std::str::from_utf8(string_bytes).map(String::from).into(),
        )
    }
}

const SHAKESPEARE: &str = r#"Now is the winter of our discontent
Made glorious summer by this sun of York.
Some are born great, some achieve greatness
And some have greatness thrust upon them.
Friends, Romans, countrymen - lend me your ears!
I come not to praise Caesar, but to bury him.
The evil that men do lives after them
The good is oft interred with their bones.
                    It is a tale
Told by an idiot, full of sound and fury
Signifying nothing.
Ay me! For aught that I could ever read,
Could ever hear by tale or history,
The course of true love never did run smooth.
I have full cause of weeping, but this heart
Shall break into a hundred thousand flaws,
Or ere I'll weep.-O Fool, I shall go mad!
                    Each your doing,
So singular in each particular,
Crowns what you are doing in the present deed,
That all your acts are queens.
"#;

#[async_std::main]
async fn main() {
    let reader = Cursor::new(Vec::from(SHAKESPEARE.as_bytes()));
    let mut framed = Framed::new(reader, LineCodec);
    let expected = SHAKESPEARE.lines().map(String::from).collect::<Vec<_>>();
    let mut actual = vec![];
    while let Some(frame) = framed.next().await.transpose().unwrap() {
        actual.push(frame);
    }
    assert_eq!(actual, expected);

    let mut actual = vec![0u8; SHAKESPEARE.as_bytes().len()];
    let expected = SHAKESPEARE.lines().map(String::from).collect::<Vec<_>>();
    let writer = Cursor::new(&mut actual);
    let mut framed = Framed::new(writer, LineCodec);
    for frame in expected {
        framed.send(frame).await.unwrap();
    }
    assert_eq!(std::str::from_utf8(&actual).unwrap(), SHAKESPEARE);
}

Re-exports

pub use crate::framed_std::*;

Modules

The Framed type for futures::io::{AsyncRead, AsyncWrite} streams

Enums

The Result of a Decode::decode call

The result of an Encode::encode call

Traits

Trait for values that can be decoded from a byte buffer

Trait for things that can be encoded to a byte buffer