Skip to content

Commit

Permalink
Merge branch 'main' into try_serialize_4
Browse files Browse the repository at this point in the history
  • Loading branch information
milyin committed Oct 17, 2024
2 parents 0acb199 + f51a5fd commit b45b906
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 12 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
16 changes: 10 additions & 6 deletions commons/zenoh-buffers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion commons/zenoh-keyexpr/src/key_expr/format/support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ impl<'s> IKeFormatStorage<'s> for Vec<Segment<'s>> {
constructor: IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError>,
segment: Segment<'s>,
) -> IterativeConstructor<Self, Self::PartialConstruct, Self::ConstructionError> {
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);
Expand Down
143 changes: 142 additions & 1 deletion zenoh-ext/src/serialization.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
use core::fmt;
//
// 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},
convert::Infallible,
fmt,
hash::Hash,
io::{Read, Write},
marker::PhantomData,
Expand All @@ -12,6 +25,7 @@ use std::{
use unwrap_infallible::UnwrapInfallible;
use zenoh::bytes::{ZBytes, ZBytesReader, ZBytesWriter};

/// Error occurring in deserialization.
#[derive(Debug)]
pub struct ZDeserializeError;
impl fmt::Display for ZDeserializeError {
Expand Down Expand Up @@ -46,6 +60,12 @@ fn default_serialize_n<T: TrySerialize>(
Ok(())
}

/// Serialization implementation. If serialization never fails,
/// the `Error` associated type should be set to `Infallible`.
///
/// See [Zenoh serialization format RFC][1].
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
pub trait TrySerialize {
type Error: std::error::Error;
fn try_serialize(&self, serializer: &mut ZSerializer) -> Result<(), Self::Error>;
Expand All @@ -58,7 +78,18 @@ pub trait TrySerialize {
}
}

/// Infallible serialization implementation. Implemeted automatically for types

Check warning on line 81 in zenoh-ext/src/serialization.rs

View workflow job for this annotation

GitHub Actions / Typos Check

"Implemeted" should be "Implemented".

Check warning on line 81 in zenoh-ext/src/serialization.rs

View workflow job for this annotation

GitHub Actions / Typos Check

"Implemeted" should be "Implemented".
/// that implement `TrySerialize<Error = Infallible>`.
/// Can be implemented manually when necessary, e.g. in case when the type already
/// have automatic implementation of `TrySerialize` with the error type not `Infallible`.
///
/// See [Zenoh serialization format RFC][1].
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
pub trait Serialize: TrySerialize {
/// 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 @@ -104,7 +135,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 @@ -122,18 +161,56 @@ 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();
t.serialize(&mut serializer);
serializer.finish()
}

/// Serialize an object according to the [Zenoh serialization format][1].
/// Returns an error if serialization fails.
///
/// Serialization doesn't take the ownership of the data.
///
/// # Examples
///
/// ```rust
/// use zenoh_ext::*;
/// let zbytes = z_try_serialize(&CString::new("new(b"Invalid utf8: \xff");
/// assert!(zbytes.is_err());
/// ```
///
/// [1]: https://github.com/eclipse-zenoh/roadmap/blob/main/rfcs/ALL/Serialization.md
pub fn z_try_serialize<T: TrySerialize + ?Sized>(t: &T) -> Result<ZBytes, T::Error> {
let mut serializer = ZSerializer::new();
t.try_serialize(&mut serializer)?;
Ok(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 @@ -143,10 +220,27 @@ 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())
}
Expand All @@ -155,6 +249,20 @@ impl ZSerializer {
&mut self.0
}

/// 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`].
/// Returns an error if serialization of an element fails.
///
/// 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 try_serialize_iter<T: TrySerialize, I: IntoIterator<Item = T>>(
&mut self,
iter: I,
Expand All @@ -170,6 +278,12 @@ impl ZSerializer {
Ok(())
}

/// 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 @@ -181,6 +295,7 @@ impl ZSerializer {
}
}

/// Finish serialization by returning a [`ZBytes`].
pub fn finish(self) -> ZBytes {
self.0.finish()
}
Expand All @@ -198,22 +313,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 @@ -226,6 +366,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

0 comments on commit b45b906

Please sign in to comment.