From 993a4e7ca4ac495c733397dcb7a881b53ab1b18d Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Wed, 11 Oct 2023 08:35:50 -0400 Subject: [PATCH] Added CanonicalSerialize/Deserialize support for VecDeque and LinkedList (#689) Co-authored-by: Pratyush Mishra --- CHANGELOG.md | 2 + serialize/src/impls.rs | 177 ++++++++++++++++++++++++++++++++++++++--- serialize/src/test.rs | 14 +++- 3 files changed, 179 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 052a28b19..92698252c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Pending +- [\#689](https://github.com/arkworks-rs/algebra/pull/689) (`ark-serialize`) Add `CanonicalSerialize` and `CanonicalDeserialize` impls for `VecDeque` and `LinkedList`. + ### Breaking changes - [\#577](https://github.com/arkworks-rs/algebra/pull/577) (`ark-ff`, `ark-ec`) Add `AdditiveGroup`, a trait for additive groups (equipped with scalar field). diff --git a/serialize/src/impls.rs b/serialize/src/impls.rs index 5e4a8a7ef..800ada441 100644 --- a/serialize/src/impls.rs +++ b/serialize/src/impls.rs @@ -1,6 +1,6 @@ use ark_std::{ - borrow::Cow, - collections::{BTreeMap, BTreeSet}, + borrow::{Borrow, Cow}, + collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, io::{Read, Write}, marker::PhantomData, rc::Rc, @@ -529,27 +529,178 @@ impl CanonicalDeserialize for Vec { } } -impl CanonicalSerialize for [T] { +// Helper function. Serializes any sequential data type to the format +// n as u64 || data[0].serialize() || ... || data[n].serialize() +#[inline] +fn serialize_seq( + seq: impl ExactSizeIterator, + mut writer: W, + compress: Compress, +) -> Result<(), SerializationError> +where + T: CanonicalSerialize, + B: Borrow, + W: Write, +{ + let len = seq.len() as u64; + len.serialize_with_mode(&mut writer, compress)?; + for item in seq { + item.borrow().serialize_with_mode(&mut writer, compress)?; + } + Ok(()) +} + +// Helper function. Describes the size of any data serialized using the above function +#[inline] +fn get_serialized_size_of_seq( + seq: impl ExactSizeIterator, + compress: Compress, +) -> usize +where + T: CanonicalSerialize, + B: Borrow, +{ + 8 + seq + .map(|item| item.borrow().serialized_size(compress)) + .sum::() +} + +impl CanonicalSerialize for VecDeque { #[inline] fn serialize_with_mode( &self, - mut writer: W, + writer: W, compress: Compress, ) -> Result<(), SerializationError> { - let len = self.len() as u64; - len.serialize_with_mode(&mut writer, compress)?; - for item in self.iter() { - item.serialize_with_mode(&mut writer, compress)?; + serialize_seq::(self.iter(), writer, compress) + } + + #[inline] + fn serialized_size(&self, compress: Compress) -> usize { + get_serialized_size_of_seq::(self.iter(), compress) + } +} + +// Identical to Valid for Vec +impl Valid for VecDeque { + #[inline] + fn check(&self) -> Result<(), SerializationError> { + T::batch_check(self.iter()) + } + + #[inline] + fn batch_check<'a>( + batch: impl Iterator + Send, + ) -> Result<(), SerializationError> + where + Self: 'a, + { + T::batch_check(batch.flat_map(|v| v.iter())) + } +} + +// Identical to CanonicalSerialize for Vec, except using the push_back() method +impl CanonicalDeserialize for VecDeque { + #[inline] + fn deserialize_with_mode( + mut reader: R, + compress: Compress, + validate: Validate, + ) -> Result { + let len = u64::deserialize_with_mode(&mut reader, compress, validate)? + .try_into() + .map_err(|_| SerializationError::NotEnoughSpace)?; + let mut values = VecDeque::with_capacity(len); + for _ in 0..len { + values.push_back(T::deserialize_with_mode( + &mut reader, + compress, + Validate::No, + )?); } - Ok(()) + + if let Validate::Yes = validate { + T::batch_check(values.iter())? + } + Ok(values) + } +} + +impl CanonicalSerialize for LinkedList { + #[inline] + fn serialize_with_mode( + &self, + writer: W, + compress: Compress, + ) -> Result<(), SerializationError> { + serialize_seq::(self.iter(), writer, compress) } #[inline] fn serialized_size(&self, compress: Compress) -> usize { - 8 + self - .iter() - .map(|item| item.serialized_size(compress)) - .sum::() + get_serialized_size_of_seq::(self.iter(), compress) + } +} + +// Identical to Valid for Vec +impl Valid for LinkedList { + #[inline] + fn check(&self) -> Result<(), SerializationError> { + T::batch_check(self.iter()) + } + + #[inline] + fn batch_check<'a>( + batch: impl Iterator + Send, + ) -> Result<(), SerializationError> + where + Self: 'a, + { + T::batch_check(batch.flat_map(|v| v.iter())) + } +} + +// Identical to CanonicalSerialize for Vec, except using the push_back() method, and the new() +// constructor. +impl CanonicalDeserialize for LinkedList { + #[inline] + fn deserialize_with_mode( + mut reader: R, + compress: Compress, + validate: Validate, + ) -> Result { + let len = u64::deserialize_with_mode(&mut reader, compress, validate)? + .try_into() + .map_err(|_| SerializationError::NotEnoughSpace)?; + let mut values = LinkedList::new(); + for _ in 0..len { + values.push_back(T::deserialize_with_mode( + &mut reader, + compress, + Validate::No, + )?); + } + + if let Validate::Yes = validate { + T::batch_check(values.iter())? + } + Ok(values) + } +} + +impl CanonicalSerialize for [T] { + #[inline] + fn serialize_with_mode( + &self, + writer: W, + compress: Compress, + ) -> Result<(), SerializationError> { + serialize_seq::(self.iter(), writer, compress) + } + + #[inline] + fn serialized_size(&self, compress: Compress) -> usize { + get_serialized_size_of_seq::(self.iter(), compress) } } diff --git a/serialize/src/test.rs b/serialize/src/test.rs index 25977357c..68f697ec2 100644 --- a/serialize/src/test.rs +++ b/serialize/src/test.rs @@ -1,6 +1,6 @@ use super::*; use ark_std::{ - collections::{BTreeMap, BTreeSet}, + collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, rand::RngCore, string::String, vec, @@ -132,6 +132,18 @@ fn test_vec() { test_serialize(Vec::::new()); } +#[test] +fn test_vecdeque() { + test_serialize([1u64, 2, 3, 4, 5].into_iter().collect::>()); + test_serialize(VecDeque::::new()); +} + +#[test] +fn test_linkedlist() { + test_serialize([1u64, 2, 3, 4, 5].into_iter().collect::>()); + test_serialize(LinkedList::::new()); +} + #[test] fn test_uint() { test_serialize(192830918usize);