algebra icon indicating copy to clipboard operation
algebra copied to clipboard

ark-serialize support for boxed::Box and borrow::Cow

Open burdges opened this issue 5 months ago • 1 comments

If we merely add T: ?Sized to Cow<'static,T> then I'd expect Cow<'static,[T]> should then serialzie exactly like [T], and hence like Vec<T>.

We want Cow<'static,[T]> to deserialize exactly like Vec<T>, which requires one new impl, but not sure if the orphan rules permit this, if an explict T: Sized helps, etc.

Box<T> could serialzie exactly like T, so then Box<[T]> would serialzie exactly like [T], and hence like Vec<T>.

Box<T> could deserialize exactly like T. As [T] has no deserializer, Box<[T]> could deserialize exactly like Vec<T>, no?

burdges avatar Jul 28 '25 00:07 burdges

If the orphan rules block this, then one work around looks like:

use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Valid, Validate, SerializationError};

use ark_std::{borrow::{Borrow,Cow,ToOwned}, convert::AsRef, io::{Read,Write}, ops::{Deref}, vec::Vec};

#[derive(Clone,Debug,Default,PartialEq,Eq,PartialOrd,Ord)]
pub struct CowVec<T: 'static+Clone>(pub Cow<'static,[T]>);

impl<T: 'static+Clone> CowVec<T> {
    pub fn owned(moo: Vec<T>) -> CowVec<T> {
        CowVec(Cow::Owned(moo))
    }
    pub const fn borrowed(moo: &'static [T]) -> CowVec<T> {
        CowVec(Cow::Borrowed(moo))
    }
}

impl<T: 'static+Clone> Deref for CowVec<T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        self.0.deref()
    }
}

impl<T: 'static+Clone> Borrow<[T]> for CowVec<T> {
    fn borrow(&self) -> &[T] {
        self.0.borrow()
    }
}

impl<T: 'static+Clone> AsRef<[T]> for CowVec<T> {
    fn as_ref(&self) -> &[T] {
        self.0.as_ref()
    }
}

impl<'a, T: 'static+Clone> IntoIterator for &'a CowVec<T> {
    type Item = &'a T;
    type IntoIter = core::slice::Iter<'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}

impl<T: 'static+Clone+CanonicalSerialize> CanonicalSerialize for CowVec<T> {
    #[inline]
    fn serialize_with_mode<W: Write>(
        &self,
        mut writer: W,
        compress: Compress,
    ) -> Result<(), SerializationError> {
        self.as_ref().serialize_with_mode(&mut writer, compress)
    }

    #[inline]
    fn serialized_size(&self, compress: Compress) -> usize {
        self.as_ref().serialized_size(compress)
    }
}

impl<T: 'static+Clone+Valid> Valid for CowVec<T> {
    #[inline]
    fn check(&self) -> Result<(), SerializationError> {
        <Vec<T>>::check(&self.as_ref().to_owned())
    }

    #[inline]
    fn batch_check<'a>(
        batch: impl Iterator<Item = &'a Self> + Send,
    ) -> Result<(), SerializationError>
    where
        Self: 'a,
    {
        let t: Vec<_> = batch.map(|v| v.as_ref().to_owned()).collect();
        <Vec<T>>::batch_check(t.iter())
    }
}

impl<T: 'static+Clone+CanonicalDeserialize> CanonicalDeserialize for CowVec<T> {
    #[inline]
    fn deserialize_with_mode<R: Read>(
        reader: R,
        compress: Compress,
        validate: Validate,
    ) -> Result<Self, SerializationError> {
        Ok(CowVec::owned(Vec::deserialize_with_mode(
            reader, compress, validate,
        )?))
    }
}

I've only used 'static here for simplicity, because the goal is to directly embed proof system parameters into contract as code, but one should favor the general Cow<'a,??> of course.

burdges avatar Jul 28 '25 01:07 burdges