From 86cbfaad6d2ae20aa0029655d9fa0fe79002da98 Mon Sep 17 00:00:00 2001 From: Joseph Perez Date: Thu, 17 Oct 2024 09:34:16 +0200 Subject: [PATCH] docs: add documentation for serialization --- zenoh-ext/src/serialization.rs | 111 +++++++++++++++++++++++++++++++-- zenoh/src/api/bytes.rs | 11 ++++ 2 files changed, 118 insertions(+), 4 deletions(-) diff --git a/zenoh-ext/src/serialization.rs b/zenoh-ext/src/serialization.rs index 2ed4e967d..2df5eaf78 100644 --- a/zenoh-ext/src/serialization.rs +++ b/zenoh-ext/src/serialization.rs @@ -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, +// use std::{ borrow::Cow, collections::{BTreeMap, BTreeSet, HashMap, HashSet}, @@ -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 { @@ -25,7 +39,15 @@ fn default_serialize_n(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) @@ -62,7 +84,15 @@ fn default_deserialize_n_uninit<'a, T: Deserialize>( Ok(unsafe { &mut *(in_place as *mut [MaybeUninit] 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; #[doc(hidden)] fn deserialize_n( @@ -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)>(&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: &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)>(&zbytes).unwrap(), (42i32, vec![1u8, 2, 3])); +/// ``` +/// +/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md pub fn z_deserialize(zbytes: &ZBytes) -> Result { let mut deserializer = ZDeserializer::new(zbytes); let t = T::deserialize(&mut deserializer)?; @@ -95,18 +149,44 @@ pub fn z_deserialize(zbytes: &ZBytes) -> Result)>(&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(&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>(&mut self, iter: I) where I::IntoIter: ExactSizeIterator, @@ -118,10 +198,7 @@ impl ZSerializer { } } - pub fn serialize_n(&mut self, ts: &[T]) { - T::serialize_n(ts, self); - } - + /// Finish serialization by returning a [`ZBytes`]. pub fn finish(self) -> ZBytes { self.0.finish() } @@ -139,22 +216,47 @@ impl From 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::().unwrap(), 42i32); +/// assert_eq!(deserializer.deserialize::>().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(&mut self) -> Result { 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, ZDeserializeError> { @@ -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, diff --git a/zenoh/src/api/bytes.rs b/zenoh/src/api/bytes.rs index 519faa340..6f6e973fc 100644 --- a/zenoh/src/api/bytes.rs +++ b/zenoh/src/api/bytes.rs @@ -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, Utf8Error> { Ok(match self.to_bytes() { Cow::Borrowed(s) => std::str::from_utf8(s)?.into(),