Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add documentation for serialization #1540

Merged
Merged
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
111 changes: 107 additions & 4 deletions zenoh-ext/src/serialization.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
//
// Copyright (c) 2024 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
//
use std::{
borrow::Cow,
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
Expand All @@ -10,6 +23,7 @@ use std::{

use zenoh::bytes::{ZBytes, ZBytesReader, ZBytesWriter};

/// Error occurring in deserialization.
#[derive(Debug)]
pub struct ZDeserializeError;
impl fmt::Display for ZDeserializeError {
Expand All @@ -25,7 +39,15 @@ fn default_serialize_n<T: Serialize>(slice: &[T], serializer: &mut ZSerializer)
}
}

/// Serialization implementation.
///
/// See [Zenoh serialization format RFC][1].
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
pub trait Serialize {
/// Serialize the given object into a [`ZSerializer`].
///
/// User may prefer to use [`ZSerializer::serialize`] instead of this function.
fn serialize(&self, serializer: &mut ZSerializer);
#[doc(hidden)]
fn serialize_n(slice: &[Self], serializer: &mut ZSerializer)
Expand Down Expand Up @@ -62,7 +84,15 @@ fn default_deserialize_n_uninit<'a, T: Deserialize>(
Ok(unsafe { &mut *(in_place as *mut [MaybeUninit<T>] as *mut [T]) })
}

/// Deserialization implementation.
///
/// See [Zenoh serialization format RFC][1].
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
pub trait Deserialize: Sized {
/// Deserialize the given type from a [`ZDeserializer`].
///
/// User may prefer to use [`ZDeserializer::deserialize`] instead of this function.
fn deserialize(deserializer: &mut ZDeserializer) -> Result<Self, ZDeserializeError>;
#[doc(hidden)]
fn deserialize_n(
Expand All @@ -80,12 +110,36 @@ pub trait Deserialize: Sized {
}
}

/// Serialize an object according to the [Zenoh serialization format][1].
///
/// Serialization doesn't take the ownership of the data.
///
/// # Examples
///
/// ```rust
/// use zenoh_ext::*;
/// let zbytes = z_serialize(&(42i32, vec![1u8, 2, 3]));
/// assert_eq!(z_deserialize::<(i32, Vec<u8>)>(&zbytes).unwrap(), (42i32, vec![1u8, 2, 3]));
/// ```
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
pub fn z_serialize<T: Serialize + ?Sized>(t: &T) -> ZBytes {
let mut serializer = ZSerializer::new();
serializer.serialize(t);
serializer.finish()
}

/// Deserialize an object according to the [Zenoh serialization format][1].
///
/// # Examples
///
/// ```rust
/// use zenoh_ext::*;
/// let zbytes = z_serialize(&(42i32, vec![1u8, 2, 3]));
/// assert_eq!(z_deserialize::<(i32, Vec<u8>)>(&zbytes).unwrap(), (42i32, vec![1u8, 2, 3]));
/// ```
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
pub fn z_deserialize<T: Deserialize>(zbytes: &ZBytes) -> Result<T, ZDeserializeError> {
let mut deserializer = ZDeserializer::new(zbytes);
let t = T::deserialize(&mut deserializer)?;
Expand All @@ -95,18 +149,44 @@ pub fn z_deserialize<T: Deserialize>(zbytes: &ZBytes) -> Result<T, ZDeserializeE
Ok(t)
}

/// Serializer implementing the [Zenoh serialization format][1].
///
/// Serializing objects one after the other is equivalent to serialize a tuple of these objects.
///
/// # Examples
///
/// ```rust
/// use zenoh_ext::*;
/// let mut serializer = ZSerializer::new();
/// serializer.serialize(42i32);
/// serializer.serialize(vec![1u8, 2, 3]);
/// let zbytes = serializer.finish();
/// assert_eq!(z_deserialize::<(i32, Vec<u8>)>(&zbytes).unwrap(), (42i32, vec![1u8, 2, 3]));
/// ```
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
#[derive(Debug)]
pub struct ZSerializer(ZBytesWriter);

impl ZSerializer {
/// Instantiate a [`ZSerializer`].
pub fn new() -> Self {
Self(ZBytes::writer())
}

/// Serialize the given object into a [`ZSerializer`].
///
/// Serialization doesn't take the ownership of the data.
pub fn serialize<T: Serialize>(&mut self, t: T) {
t.serialize(self)
}

/// Serialize the given iterator into a [`ZSerializer`].
///
/// Sequence serialized with this method may be deserialized with [`ZDeserializer::deserialize_iter`].
/// See [Zenoh serialization format RFC][1].
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md#sequences
pub fn serialize_iter<T: Serialize, I: IntoIterator<Item = T>>(&mut self, iter: I)
where
I::IntoIter: ExactSizeIterator,
Expand All @@ -118,10 +198,7 @@ impl ZSerializer {
}
}

pub fn serialize_n<T: Serialize>(&mut self, ts: &[T]) {
T::serialize_n(ts, self);
}

/// Finish serialization by returning a [`ZBytes`].
pub fn finish(self) -> ZBytes {
self.0.finish()
}
Expand All @@ -139,22 +216,47 @@ impl From<ZSerializer> for ZBytes {
}
}

/// Deserializer implementing the [Zenoh serialization format][1].
///
/// Deserializing objects one after the other is equivalent to serialize a tuple of these objects.
///
/// # Examples
///
/// ```rust
/// use zenoh_ext::*;
/// let zbytes = z_serialize(&(42i32, vec![1u8, 2, 3]));
/// let mut deserializer = ZDeserializer::new(&zbytes);
/// assert_eq!(deserializer.deserialize::<i32>().unwrap(), 42i32);
/// assert_eq!(deserializer.deserialize::<Vec<u8>>().unwrap(), vec![1u8, 2, 3]);
/// assert!(deserializer.done())
/// ```
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
#[derive(Debug)]
pub struct ZDeserializer<'a>(ZBytesReader<'a>);

impl<'a> ZDeserializer<'a> {
/// Instantiate a [`ZDeserializer`] from a [`ZBytes`].
pub fn new(zbytes: &'a ZBytes) -> Self {
Self(zbytes.reader())
}

/// Return true if there is no data left to deserialize.
pub fn done(&self) -> bool {
self.0.is_empty()
}

/// Deserialize the given type from a [`ZDeserializer`].
pub fn deserialize<T: Deserialize>(&mut self) -> Result<T, ZDeserializeError> {
T::deserialize(self)
}

/// Deserialize an iterator into a [`ZDeserializer`].
///
/// Sequence deserialized with this method may have been serialized with [`ZSerializer::serialize_iter`].
/// See [Zenoh serialization format RFC][1].
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md#sequences
pub fn deserialize_iter<'b, T: Deserialize>(
&'b mut self,
) -> Result<ZReadIter<'a, 'b, T>, ZDeserializeError> {
Expand All @@ -167,6 +269,7 @@ impl<'a> ZDeserializer<'a> {
}
}

/// Iterator returned by [`ZDeserializer::deserialize_iter`].
pub struct ZReadIter<'a, 'b, T: Deserialize> {
deserializer: &'b mut ZDeserializer<'a>,
len: usize,
Expand Down
11 changes: 11 additions & 0 deletions zenoh/src/api/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,21 @@ impl ZBytes {
self.0.len()
}

/// Access raw bytes contained in the [`ZBytes`].
///
/// In the case `ZBytes` contains non-contiguous regions of memory, an allocation and a copy
/// will be done, that's why the method returns a [`Cow`].
/// It's also possible to use [`ZBytes::slices`] instead to avoid this copy.
pub fn to_bytes(&self) -> Cow<[u8]> {
self.0.contiguous()
}

/// Try to access a string contained in the [`ZBytes`], fail if it contains non-UTF8 bytes.
///
/// In the case `ZBytes` contains non-contiguous regions of memory, an allocation and a copy
/// will be done, that's why the method returns a [`Cow`].
/// It's also possible to use [`ZBytes::slices`] instead to avoid this copy, but then the UTF8
/// check has to be done manually.
pub fn try_to_string(&self) -> Result<Cow<str>, Utf8Error> {
Ok(match self.to_bytes() {
Cow::Borrowed(s) => std::str::from_utf8(s)?.into(),
Expand Down
Loading