From 86cbfaad6d2ae20aa0029655d9fa0fe79002da98 Mon Sep 17 00:00:00 2001 From: Joseph Perez Date: Thu, 17 Oct 2024 09:34:16 +0200 Subject: [PATCH 1/4] 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(), From f397ec7d3a6ae8c6923a3d4feec3283a82c0a0c2 Mon Sep 17 00:00:00 2001 From: Mahmoud Mazouz Date: Thu, 17 Oct 2024 07:41:39 +0000 Subject: [PATCH 2/4] Fix Clippy errors from Rust 1.82.0 It is possible to preview unreleased rust versions like so: ```console RUSTUP_DIST_SERVER=https://dev-static.rust-lang.org rustup install 1.82.0 ``` --- commons/zenoh-buffers/src/lib.rs | 16 ++++++++++------ .../zenoh-keyexpr/src/key_expr/format/support.rs | 6 +++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/commons/zenoh-buffers/src/lib.rs b/commons/zenoh-buffers/src/lib.rs index 102495889..5684af7d4 100644 --- a/commons/zenoh-buffers/src/lib.rs +++ b/commons/zenoh-buffers/src/lib.rs @@ -59,17 +59,21 @@ macro_rules! unsafe_slice_mut { #[cfg(all(not(test), not(feature = "test")))] #[macro_export] macro_rules! unsafe_slice { - ($s:expr,$r:expr) => { - unsafe { $s.get_unchecked($r) } - }; + ($s:expr,$r:expr) => {{ + let slice = &*$s; + let index = $r; + unsafe { slice.get_unchecked(index) } + }}; } #[cfg(all(not(test), not(feature = "test")))] #[macro_export] macro_rules! unsafe_slice_mut { - ($s:expr,$r:expr) => { - unsafe { $s.get_unchecked_mut($r) } - }; + ($s:expr,$r:expr) => {{ + let slice = &mut *$s; + let index = $r; + unsafe { slice.get_unchecked_mut(index) } + }}; } pub mod buffer { diff --git a/commons/zenoh-keyexpr/src/key_expr/format/support.rs b/commons/zenoh-keyexpr/src/key_expr/format/support.rs index 16451797a..65b534dc2 100644 --- a/commons/zenoh-keyexpr/src/key_expr/format/support.rs +++ b/commons/zenoh-keyexpr/src/key_expr/format/support.rs @@ -246,7 +246,11 @@ impl<'s> IKeFormatStorage<'s> for Vec> { constructor: IterativeConstructor, segment: Segment<'s>, ) -> IterativeConstructor { - let IterativeConstructor::Complete(mut this) = constructor else { + // NOTE(fuzzypixelz): Rust 1.82.0 can detect that this pattern is irrefutable but that's not + // necessarily the case for prior versions. Thus we silence this lint to keep the MSRV minimal. + #[allow(irrefutable_let_patterns)] + let IterativeConstructor::Complete(mut this) = constructor + else { unsafe { core::hint::unreachable_unchecked() } }; this.push(segment); From 45c0740488c21e0ea32b9af267ad6bdd71668009 Mon Sep 17 00:00:00 2001 From: Luca Cominardi Date: Thu, 17 Oct 2024 11:44:08 +0200 Subject: [PATCH 3/4] Fix rust-cache invocation order in CI (#1542) --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7f3f3215..5d11cc735 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,12 +39,12 @@ jobs: - name: Clone this repository uses: actions/checkout@v4 - - name: Setup rust-cache - uses: Swatinem/rust-cache@v2 - - name: Update Stable Rust toolchain run: rustup update stable + - name: Setup rust-cache + uses: Swatinem/rust-cache@v2 + - name: Install latest cargo-deny uses: taiki-e/install-action@cargo-deny From f51a5fdc0d866e24d41ac576329626dda45624df Mon Sep 17 00:00:00 2001 From: oteffahi <70609372+oteffahi@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:53:29 +0200 Subject: [PATCH 4/4] Fix pre-release.yml workflow (#1543) --- .github/workflows/pre-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index bb245d474..0a8588c03 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -53,7 +53,7 @@ jobs: run: rustup component add rustfmt clippy - name: Code format check - run: cargo fmt --check -- cargo fmt --check -- --config "unstable_features=true,imports_granularity=Crate,group_imports=StdExternalCrate" + run: cargo fmt --check -- --config "unstable_features=true,imports_granularity=Crate,group_imports=StdExternalCrate" env: CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse