Skip to content
This repository has been archived by the owner on Aug 15, 2021. It is now read-only.

Add tokio_util::codec::{Encoder, Decoder} support #202

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# see https://editorconfig.org for more options, and setup instructions for yours editor

[*.rs]
indent_style = space
indent_size = 4
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ maintenance = { status = "passively-maintained" }
[dependencies]
half = "1.2.0"
serde = { version = "1.0.14", default-features = false }
tokio-util = { version = "0.3", optional = true }
bytes = { version = "0.5", optional = true }

[dev-dependencies]
serde_derive = { version = "1.0.14", default-features = false }
Expand All @@ -31,3 +33,4 @@ alloc = ["serde/alloc"]
std = ["serde/std" ]
unsealed_read_write = []
tags = []
codec = ["tokio-util/codec", "bytes"]
85 changes: 85 additions & 0 deletions src/codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! Encoding and decoding for tokio

use crate::error::Category;
use crate::Error;

use std::marker::PhantomData;

use bytes::{buf::ext::BufMutExt, buf::Buf, BytesMut};
use serde::de::DeserializeOwned;
use serde::Serialize;
use tokio_util::codec;

/// A `tokio_util::codec::Encoder` for CBOR frames
pub struct Encoder<T: Serialize>(PhantomData<T>);

impl<T: Serialize> Default for Encoder<T> {
fn default() -> Self {
Self(PhantomData)
}
}

impl<T: Serialize> codec::Encoder<&T> for Encoder<T> {
type Error = Error;

fn encode(&mut self, item: &T, dst: &mut BytesMut) -> Result<(), Error> {
crate::to_writer(dst.writer(), item)
}
}

/// A `tokio_util::codec::Decoder` for CBOR frames
pub struct Decoder<T: DeserializeOwned>(PhantomData<T>);

impl<T: DeserializeOwned> Default for Decoder<T> {
fn default() -> Self {
Self(PhantomData)
}
}

impl<T: DeserializeOwned> codec::Decoder for Decoder<T> {
type Item = T;
type Error = Error;

fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Error> {
let mut bytes: &[u8] = src.as_ref();
let starting = bytes.len();

let item: T = match crate::from_reader(&mut bytes) {
Err(e) if e.classify() == Category::Eof => return Ok(None),
Ok(v) => v,
e => e?,
};

let ending = bytes.len();
src.advance(starting - ending);
Ok(Some(item))
}
}

/// A Codec for CBOR frames
pub struct Codec<T: Serialize, U: DeserializeOwned>(Encoder<T>, Decoder<U>);

impl<T: Serialize, U: DeserializeOwned> Default for Codec<T, U> {
fn default() -> Self {
Codec(Encoder::default(), Decoder::default())
}
}

impl<T: Serialize, U: DeserializeOwned> codec::Encoder<&T> for Codec<T, U> {
type Error = Error;

#[inline]
fn encode(&mut self, item: &T, dst: &mut BytesMut) -> Result<(), Error> {
self.0.encode(item, dst)
}
}

impl<T: Serialize, U: DeserializeOwned> codec::Decoder for Codec<T, U> {
type Item = U;
type Error = Error;

#[inline]
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Error> {
self.1.decode(src)
}
}
65 changes: 63 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,20 @@
//! # Packed Encoding
//! When serializing structs or enums in CBOR the keys or enum variant names will be serialized
//! as string keys to a map. Especially in embedded environments this can increase the file
//! size too much. In packed encoding the keys and variants will be serialized as variable sized
//! integers. The first 24 entries in any struct consume only a single byte!
//! size too much. In packed encoding all struct keys, as well as any enum variant that has no data,
//! will be serialized as variable sized integers. The first 24 entries in any struct consume only a
//! single byte! Packed encoding uses serde's preferred [externally tagged enum
//! format](https://serde.rs/enum-representations.html) and therefore serializes enum variant names
//! as string keys when that variant contains data. So, in the packed encoding example, `FirstVariant`
//! encodes to a single byte, but encoding `SecondVariant` requires 16 bytes.
//!
//! To serialize a document in this format use `Serializer::new(writer).packed_format()` or
//! the shorthand `ser::to_vec_packed`. The deserialization works without any changes.
//!
//! If you would like to omit the enum variant encoding for all variants, including ones that
//! contain data, you can add `legacy_enums()` in addition to `packed_format()`, as can seen
//! in the Serialize using minimal encoding example.
//!
//! # Self describing documents
//! In some contexts different formats are used but there is no way to declare the format used
//! out of band. For this reason CBOR has a magic number that may be added before any document.
Expand Down Expand Up @@ -132,6 +141,55 @@
//! # }
//! ```
//!
//! Serialize using packed encoding
//!
//! ```rust
//! use serde_derive::{Deserialize, Serialize};
//! use serde_cbor::ser::to_vec_packed;
//! use WithTwoVariants::*;
//!
//! #[derive(Debug, Serialize, Deserialize)]
//! enum WithTwoVariants {
//! FirstVariant,
//! SecondVariant(u8),
//! }
//!
//! let cbor = to_vec_packed(&FirstVariant).unwrap();
//! assert_eq!(cbor.len(), 1);
//!
//! let cbor = to_vec_packed(&SecondVariant(0)).unwrap();
//! assert_eq!(cbor.len(), 16); // Includes 13 bytes of "SecondVariant"
//! ```
//!
//! Serialize using minimal encoding
//!
//! ```rust
//! use serde_derive::{Deserialize, Serialize};
//! use serde_cbor::{Result, Serializer, ser::{self, IoWrite}};
//! use WithTwoVariants::*;
//!
//! fn to_vec_minimal<T>(value: &T) -> Result<Vec<u8>>
//! where
//! T: serde::Serialize,
//! {
//! let mut vec = Vec::new();
//! value.serialize(&mut Serializer::new(&mut IoWrite::new(&mut vec)).packed_format().legacy_enums())?;
//! Ok(vec)
//! }
//!
//! #[derive(Debug, Serialize, Deserialize)]
//! enum WithTwoVariants {
//! FirstVariant,
//! SecondVariant(u8),
//! }
//!
//! let cbor = to_vec_minimal(&FirstVariant).unwrap();
//! assert_eq!(cbor.len(), 1);
//!
//! let cbor = to_vec_minimal(&SecondVariant(0)).unwrap();
//! assert_eq!(cbor.len(), 3);
//! ```
//!
//! # `no-std` support
//!
//! Serde CBOR supports building in a `no_std` context, use the following lines
Expand Down Expand Up @@ -277,6 +335,9 @@ mod write;
#[cfg(feature = "std")]
pub mod value;

#[cfg(feature = "codec")]
pub mod codec;

// Re-export the [items recommended by serde](https://serde.rs/conventions.html).
#[doc(inline)]
pub use crate::de::{Deserializer, StreamDeserializer};
Expand Down
3 changes: 1 addition & 2 deletions src/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ fn untagged<T>(value: T) -> Tagged<T> {

macro_rules! delegate {
($name: ident, $type: ty) => {
fn $name<E: serde::de::Error>(self, v: $type) -> Result<Self::Value, E>
{
fn $name<E: serde::de::Error>(self, v: $type) -> Result<Self::Value, E> {
T::deserialize(v.into_deserializer()).map(untagged)
}
};
Expand Down
47 changes: 47 additions & 0 deletions tests/codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#![cfg(feature = "codec")]

use bytes::{BufMut, BytesMut};
use serde_cbor::{codec::Codec, error::Category};
use tokio_util::codec::{Decoder, Encoder};

#[test]
fn decode() {
let mut codec = Codec::<u8, u8>::default();

assert_eq!(
codec
.decode(&mut b"\xFF"[..].into())
.err()
.unwrap()
.classify(),
Category::Syntax
);

assert_eq!(
codec
.decode(&mut b"\x24"[..].into())
.err()
.unwrap()
.classify(),
Category::Data
);

assert_eq!(codec.decode(&mut b"\x07"[..].into()).unwrap().unwrap(), 7);

let mut buf = BytesMut::with_capacity(2);
assert_eq!(codec.decode(&mut buf).unwrap(), None);
buf.put_u8(0x18);
assert_eq!(codec.decode(&mut buf).unwrap(), None);
buf.put_u8(0x18);
assert_eq!(codec.decode(&mut buf).unwrap(), Some(24));
}

#[test]
fn encode() {
let mut codec = Codec::<u8, u8>::default();
let mut buf = BytesMut::new();

codec.encode(&7, &mut buf).unwrap();
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], 7);
}