Skip to content

Commit

Permalink
Refine encoder
Browse files Browse the repository at this point in the history
  • Loading branch information
Mallets committed Feb 21, 2024
1 parent 2d93268 commit 1e1eef3
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 57 deletions.
2 changes: 1 addition & 1 deletion commons/zenoh-protocol/src/core/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// ZettaScale Zenoh Team, <[email protected]>
//
use crate::core::CowStr;
use alloc::{borrow::Cow, string::String};
use alloc::borrow::Cow;
use core::fmt::{self, Debug};
use zenoh_result::{bail, ZResult};

Expand Down
132 changes: 115 additions & 17 deletions zenoh/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,9 @@ use zenoh_shm::SharedMemoryBuf;
pub trait EncodingMapping {
fn prefix_to_str(&self, e: EncodingPrefix) -> &str;
fn str_to_prefix(&self, s: &str) -> EncodingPrefix;
fn parse(s: &str) -> ZResult<Encoding>;
}

pub trait Encoder<T> {
fn encode(t: T) -> Value;
}

pub trait Decoder<T> {
fn decode(t: &Value) -> ZResult<T>;
fn parse<S>(s: S) -> ZResult<Encoding>
where
S: Into<String>;
}

/// Default encoder provided with Zenoh to facilitate the encoding and decoding
Expand All @@ -43,9 +37,35 @@ pub trait Decoder<T> {
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct DefaultEncoder;

pub struct DefaultMapping;

impl DefaultMapping {
pub const EMPTY: EncodingPrefix = 0;
pub const APP_OCTET_STREAM: EncodingPrefix = 1;
pub const APP_CUSTOM: EncodingPrefix = 2;
pub const TEXT_PLAIN: EncodingPrefix = 3;
pub const APP_PROPERTIES: EncodingPrefix = 4;
pub const APP_JSON: EncodingPrefix = 5;
pub const APP_SQL: EncodingPrefix = 6;
pub const APP_INTEGER: EncodingPrefix = 7;
pub const APP_FLOAT: EncodingPrefix = 8;
pub const APP_XML: EncodingPrefix = 9;
pub const APP_XHTML_XML: EncodingPrefix = 10;
pub const APP_XWWW_FORM_URLENCODED: EncodingPrefix = 11;
pub const TEXT_JSON: EncodingPrefix = 12;
pub const TEXT_HTML: EncodingPrefix = 13;
pub const TEXT_XML: EncodingPrefix = 14;
pub const TEXT_CSS: EncodingPrefix = 15;
pub const TEXT_CSV: EncodingPrefix = 16;
pub const TEXT_JAVASCRIPT: EncodingPrefix = 17;
pub const IMAGE_JPEG: EncodingPrefix = 18;
pub const IMAGE_PNG: EncodingPrefix = 19;
pub const IMAGE_GIF: EncodingPrefix = 20;
}

impl DefaultEncoder {
pub const EMPTY: Encoding = Encoding::new(0);
pub const APP_OCTET_STREAM: Encoding = Encoding::new(1);
pub const EMPTY: Encoding = Encoding::new(DefaultMapping::EMPTY);
pub const APP_OCTET_STREAM: Encoding = Encoding::new(DefaultMapping::APP_OCTET_STREAM);
pub const APP_CUSTOM: Encoding = Encoding::new(2);
pub const TEXT_PLAIN: Encoding = Encoding::new(3);
pub const APP_PROPERTIES: Encoding = Encoding::new(4);
Expand Down Expand Up @@ -137,17 +157,35 @@ impl EncodingMapping for DefaultEncoder {

// Parse a string into a valid Encoding. This functions performs the necessary
// prefix mapping and suffix substring when parsing the input.
fn parse(t: &str) -> ZResult<Encoding> {
for (s, p) in Self::KNOWN_STRING.entries() {
if let Some((_, b)) = t.split_once(s) {
return Encoding::new(*p).with_suffix(b.to_string());
fn parse<S>(t: S) -> ZResult<Encoding>
where
S: Into<String>,
{
fn _parse(mut t: String) -> ZResult<Encoding> {
if t.is_empty() {
return Ok(DefaultEncoder::EMPTY);
}

// Skip empty string mapping
for (s, p) in DefaultEncoder::KNOWN_STRING.entries().skip(1) {
if let Some(i) = t.find(s) {
return Encoding::new(*p).with_suffix(t.split_off(i + s.len()));
}
}
DefaultEncoder::EMPTY.with_suffix(t.to_string())
}
Encoding::empty().with_suffix(t.to_string())
_parse(t.into())
}
}

// -- impls
// Encoder
pub trait Encoder<T> {
fn encode(t: T) -> Value;
}

pub trait Decoder<T> {
fn decode(t: &Value) -> ZResult<T>;
}

// Bytes conversion
impl Encoder<ZBuf> for DefaultEncoder {
Expand Down Expand Up @@ -384,3 +422,63 @@ impl Encoder<SharedMemoryBuf> for DefaultEncoder {
}
}
}

mod tests {
#[test]
fn encoder() {
use crate::Value;
use zenoh_buffers::ZBuf;
use zenoh_collections::Properties;

macro_rules! encode_decode {
($t:ty, $in:expr) => {
let t = $in.clone();
let v = Value::encode(t);
let out: $t = v.decode().unwrap();
assert_eq!($in, out)
};
}

encode_decode!(u8, u8::MIN);
encode_decode!(u16, u16::MIN);
encode_decode!(u32, u32::MIN);
encode_decode!(u64, u64::MIN);
encode_decode!(usize, usize::MIN);

encode_decode!(u8, u8::MAX);
encode_decode!(u16, u16::MAX);
encode_decode!(u32, u32::MAX);
encode_decode!(u64, u64::MAX);
encode_decode!(usize, usize::MAX);

encode_decode!(i8, i8::MIN);
encode_decode!(i16, i16::MIN);
encode_decode!(i32, i32::MIN);
encode_decode!(i64, i64::MIN);
encode_decode!(isize, isize::MIN);

encode_decode!(i8, i8::MAX);
encode_decode!(i16, i16::MAX);
encode_decode!(i32, i32::MAX);
encode_decode!(i64, i64::MAX);
encode_decode!(isize, isize::MAX);

encode_decode!(f32, f32::MIN);
encode_decode!(f64, f64::MIN);

encode_decode!(f32, f32::MAX);
encode_decode!(f64, f64::MAX);

encode_decode!(String, "");
encode_decode!(String, String::from("abcdefghijklmnopqrstuvwxyz"));

encode_decode!(Vec<u8>, vec![0u8; 0]);
encode_decode!(Vec<u8>, vec![0u8; 64]);

encode_decode!(ZBuf, ZBuf::from(vec![0u8; 0]));
encode_decode!(ZBuf, ZBuf::from(vec![0u8; 64]));

encode_decode!(Properties, Properties::from(""));
encode_decode!(Properties, Properties::from("a=1;b=2"));
}
}
175 changes: 136 additions & 39 deletions zenoh/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,84 +31,157 @@ pub struct Value {
}

impl Value {
/// Creates a new zenoh Value.
/// Creates a new [`Value`].
pub fn new(payload: ZBuf) -> Self {
Value {
payload,
encoding: Encoding::empty(),
}
}

/// Creates an empty Value.
/// Creates an empty [`Value`].
pub fn empty() -> Self {
Value {
payload: ZBuf::empty(),
encoding: Encoding::empty(),
}
}

/// Sets the encoding of this zenoh Value.
/// Sets the payload of this [`Value`]`.
#[inline(always)]
pub fn encoding(mut self, encoding: Encoding) -> Self {
self.encoding = encoding;
pub fn payload(mut self, payload: ZBuf) -> Self {
self.payload = payload;
self
}
}

impl std::fmt::Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Value{{ payload: {:?}, encoding: {} }}",
self.payload, self.encoding
)
}
}

impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let payload = self.payload.contiguous();
write!(
f,
"{}",
String::from_utf8(payload.clone().into_owned())
.unwrap_or_else(|_| b64_std_engine.encode(payload))
)
/// Sets the encoding of this [`Value`]`.
#[inline(always)]
pub fn encoding(mut self, encoding: Encoding) -> Self {
self.encoding = encoding;
self
}
}

impl std::error::Error for Value {}

// Provide some facilities in the Rust API to encode/decode [`Value`] with an `Encoder`.
impl Value {
pub fn decode<T>(&self) -> ZResult<T>
/// Encode an object of type `T` as a [`Value`] using the [`DefaultEncoder`].
///
/// ```rust
/// use zenoh::value::Value;
///
/// let start = String::from("abc");
/// let value = Value::encode(start.clone());
/// let end: String = value.decode().unwrap();
/// assert_eq!(start, end);
/// ```
pub fn encode<T>(t: T) -> Self
where
DefaultEncoder: Decoder<T>,
DefaultEncoder: Encoder<T>,
{
DefaultEncoder::decode(self)
DefaultEncoder::encode(t)
}

pub fn decode_with<T, M>(&self) -> ZResult<T>
/// Encode an object of type `T` as a [`Value`] using a provided [`Encoder`].
///
/// ```rust
/// use zenoh::prelude::sync::*;
/// use zenoh_result::{zerror, ZResult};
/// use zenoh::encoding::{Encoder, Decoder};
///
/// struct MyEncoder;
///
/// impl MyEncoder {
/// pub const STRING: Encoding = Encoding::new(2);
/// }
///
/// impl Encoder<String> for MyEncoder {
/// fn encode(s: String) -> Value {
/// Value::new(s.into_bytes().into()).encoding(MyEncoder::STRING)
/// }
/// }
///
/// impl Decoder<String> for MyEncoder {
/// fn decode(v: &Value) -> ZResult<String> {
/// if v.encoding == MyEncoder::STRING {
/// String::from_utf8(v.payload.contiguous().to_vec()).map_err(|e| zerror!("{}", e).into())
/// } else {
/// Err(zerror!("Invalid encoding").into())
/// }
/// }
/// }
///
/// let start = String::from("abc");
/// let value = Value::encode_with::<MyEncoder, _>(start.clone());
/// let end: String = value.decode_with::<MyEncoder, _>().unwrap();
/// assert_eq!(start, end);
/// ```
pub fn encode_with<M, T>(t: T) -> Self
where
M: Decoder<T>,
M: Encoder<T>,
{
M::decode(self)
M::encode(t)
}

pub fn encode<T>(t: T) -> Self
/// Decode an object of type `T` from a [`Value`] using the [`DefaultEncoder`].
///
/// ```rust
/// use zenoh::value::Value;
///
/// let start = String::from("abc");
/// let value = Value::encode(start.clone());
/// let end: String = value.decode().unwrap();
/// assert_eq!(start, end);
/// ```
pub fn decode<T>(&self) -> ZResult<T>
where
DefaultEncoder: Encoder<T>,
DefaultEncoder: Decoder<T>,
{
DefaultEncoder::encode(t)
DefaultEncoder::decode(self)
}

pub fn encode_with<T, M>(t: T) -> Self
/// Decode an object of type `T` from a [`Value`] using a provided [`Encoder`].
///
/// ```rust
/// use zenoh::prelude::sync::*;
/// use zenoh_result::{zerror, ZResult};
/// use zenoh::encoding::{Encoder, Decoder};
///
/// struct MyEncoder;
///
/// impl MyEncoder {
/// pub const STRING: Encoding = Encoding::new(2);
/// }
///
/// impl Encoder<String> for MyEncoder {
/// fn encode(s: String) -> Value {
/// Value::new(s.into_bytes().into()).encoding(MyEncoder::STRING)
/// }
/// }
///
/// impl Decoder<String> for MyEncoder {
/// fn decode(v: &Value) -> ZResult<String> {
/// if v.encoding == MyEncoder::STRING {
/// String::from_utf8(v.payload.contiguous().to_vec()).map_err(|e| zerror!("{}", e).into())
/// } else {
/// Err(zerror!("Invalid encoding").into())
/// }
/// }
/// }
///
/// let start = String::from("abc");
/// let value = Value::encode_with::<MyEncoder, _>(start.clone());
/// let end: String = value.decode_with::<MyEncoder, _>().unwrap();
/// assert_eq!(start, end);
/// ```
pub fn decode_with<M, T>(&self) -> ZResult<T>
where
M: Encoder<T>,
M: Decoder<T>,
{
M::encode(t)
M::decode(self)
}
}

/// Build a [`Value`] from any type `T` supported by the [`DefaultEncoder`].
impl<T> From<T> for Value
where
DefaultEncoder: Encoder<T>,
Expand All @@ -117,3 +190,27 @@ where
Value::encode(t)
}
}

impl std::fmt::Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Value{{ payload: {:?}, encoding: {} }}",
self.payload, self.encoding
)
}
}

impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let payload = self.payload.contiguous();
write!(
f,
"{}",
String::from_utf8(payload.clone().into_owned())
.unwrap_or_else(|_| b64_std_engine.encode(payload))
)
}
}

impl std::error::Error for Value {}

0 comments on commit 1e1eef3

Please sign in to comment.