dory_pcs/primitives/
serialization.rs

1//! Serialization primitives for Dory types
2
3#![allow(missing_docs)]
4#![allow(clippy::missing_errors_doc)]
5#![allow(clippy::missing_panics_doc)]
6
7use std::io::{Read, Write};
8
9// Re-export derive macros
10pub use dory_derive::{DoryDeserialize, DorySerialize, Valid};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Compress {
14    Yes,
15    No,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Validate {
20    Yes,
21    No,
22}
23
24#[derive(Debug, thiserror::Error)]
25pub enum SerializationError {
26    #[error("IO error: {0}")]
27    IoError(#[from] std::io::Error),
28
29    #[error("Invalid data: {0}")]
30    InvalidData(String),
31
32    #[error("Unexpected data")]
33    UnexpectedData,
34}
35
36/// Trait for validating deserialized data.
37/// This is checked after deserialization when `Validate::Yes` is used.
38pub trait Valid {
39    /// Check that the current value is valid (e.g., in the correct subgroup).
40    fn check(&self) -> Result<(), SerializationError>;
41
42    /// Batch check for efficiency when validating multiple elements.
43    fn batch_check<'a>(batch: impl Iterator<Item = &'a Self>) -> Result<(), SerializationError>
44    where
45        Self: 'a,
46    {
47        for item in batch {
48            item.check()?;
49        }
50        Ok(())
51    }
52}
53
54/// Serializer in little endian format.
55pub trait DorySerialize {
56    /// Serialize with customization flags.
57    fn serialize_with_mode<W: Write>(
58        &self,
59        writer: W,
60        compress: Compress,
61    ) -> Result<(), SerializationError>;
62
63    /// Returns the serialized size in bytes for the given compression mode.
64    fn serialized_size(&self, compress: Compress) -> usize;
65
66    /// Serialize in compressed form.
67    fn serialize_compressed<W: Write>(&self, writer: W) -> Result<(), SerializationError> {
68        self.serialize_with_mode(writer, Compress::Yes)
69    }
70
71    /// Returns the compressed size in bytes.
72    fn compressed_size(&self) -> usize {
73        self.serialized_size(Compress::Yes)
74    }
75
76    /// Serialize in uncompressed form.
77    fn serialize_uncompressed<W: Write>(&self, writer: W) -> Result<(), SerializationError> {
78        self.serialize_with_mode(writer, Compress::No)
79    }
80
81    /// Returns the uncompressed size in bytes.
82    fn uncompressed_size(&self) -> usize {
83        self.serialized_size(Compress::No)
84    }
85}
86
87/// Deserializer in little endian format.
88pub trait DoryDeserialize {
89    /// Deserialize with customization flags.
90    fn deserialize_with_mode<R: Read>(
91        reader: R,
92        compress: Compress,
93        validate: Validate,
94    ) -> Result<Self, SerializationError>
95    where
96        Self: Sized;
97
98    /// Deserialize from compressed form with validation.
99    fn deserialize_compressed<R: Read>(reader: R) -> Result<Self, SerializationError>
100    where
101        Self: Sized,
102    {
103        Self::deserialize_with_mode(reader, Compress::Yes, Validate::Yes)
104    }
105
106    /// Deserialize from compressed form without validation.
107    ///
108    /// # Safety
109    /// This skips validation checks. Use only when you trust the input source.
110    fn deserialize_compressed_unchecked<R: Read>(reader: R) -> Result<Self, SerializationError>
111    where
112        Self: Sized,
113    {
114        Self::deserialize_with_mode(reader, Compress::Yes, Validate::No)
115    }
116
117    /// Deserialize from uncompressed form with validation.
118    fn deserialize_uncompressed<R: Read>(reader: R) -> Result<Self, SerializationError>
119    where
120        Self: Sized,
121    {
122        Self::deserialize_with_mode(reader, Compress::No, Validate::Yes)
123    }
124
125    /// Deserialize from uncompressed form without validation.
126    ///
127    /// # Safety
128    /// This skips validation checks. Use only when you trust the input source.
129    fn deserialize_uncompressed_unchecked<R: Read>(reader: R) -> Result<Self, SerializationError>
130    where
131        Self: Sized,
132    {
133        Self::deserialize_with_mode(reader, Compress::No, Validate::No)
134    }
135}
136
137mod primitive_impls {
138    use super::*;
139
140    macro_rules! impl_primitive_serialization {
141        ($t:ty, $size:expr) => {
142            impl Valid for $t {
143                fn check(&self) -> Result<(), SerializationError> {
144                    // Primitives are always valid
145                    Ok(())
146                }
147            }
148
149            impl DorySerialize for $t {
150                fn serialize_with_mode<W: Write>(
151                    &self,
152                    mut writer: W,
153                    _compress: Compress,
154                ) -> Result<(), SerializationError> {
155                    writer.write_all(&self.to_le_bytes())?;
156                    Ok(())
157                }
158
159                fn serialized_size(&self, _compress: Compress) -> usize {
160                    $size
161                }
162            }
163
164            impl DoryDeserialize for $t {
165                fn deserialize_with_mode<R: Read>(
166                    mut reader: R,
167                    _compress: Compress,
168                    _validate: Validate,
169                ) -> Result<Self, SerializationError> {
170                    let mut bytes = [0u8; $size];
171                    reader.read_exact(&mut bytes)?;
172                    Ok(<$t>::from_le_bytes(bytes))
173                }
174            }
175        };
176    }
177
178    impl_primitive_serialization!(u8, 1);
179    impl_primitive_serialization!(u16, 2);
180    impl_primitive_serialization!(u32, 4);
181    impl_primitive_serialization!(u64, 8);
182    impl_primitive_serialization!(usize, std::mem::size_of::<usize>());
183    impl_primitive_serialization!(i8, 1);
184    impl_primitive_serialization!(i16, 2);
185    impl_primitive_serialization!(i32, 4);
186    impl_primitive_serialization!(i64, 8);
187
188    impl Valid for bool {
189        fn check(&self) -> Result<(), SerializationError> {
190            Ok(())
191        }
192    }
193
194    impl DorySerialize for bool {
195        fn serialize_with_mode<W: Write>(
196            &self,
197            mut writer: W,
198            _compress: Compress,
199        ) -> Result<(), SerializationError> {
200            writer.write_all(&[*self as u8])?;
201            Ok(())
202        }
203
204        fn serialized_size(&self, _compress: Compress) -> usize {
205            1
206        }
207    }
208
209    impl DoryDeserialize for bool {
210        fn deserialize_with_mode<R: Read>(
211            mut reader: R,
212            _compress: Compress,
213            _validate: Validate,
214        ) -> Result<Self, SerializationError> {
215            let mut byte = [0u8; 1];
216            reader.read_exact(&mut byte)?;
217            match byte[0] {
218                0 => Ok(false),
219                1 => Ok(true),
220                _ => Err(SerializationError::InvalidData(
221                    "Invalid bool value".to_string(),
222                )),
223            }
224        }
225    }
226
227    impl<T: Valid> Valid for Vec<T> {
228        fn check(&self) -> Result<(), SerializationError> {
229            for item in self {
230                item.check()?;
231            }
232            Ok(())
233        }
234    }
235
236    impl<T: DorySerialize> DorySerialize for Vec<T> {
237        fn serialize_with_mode<W: Write>(
238            &self,
239            mut writer: W,
240            compress: Compress,
241        ) -> Result<(), SerializationError> {
242            (self.len() as u64).serialize_with_mode(&mut writer, compress)?;
243            for item in self {
244                item.serialize_with_mode(&mut writer, compress)?;
245            }
246            Ok(())
247        }
248
249        fn serialized_size(&self, compress: Compress) -> usize {
250            let len_size = 8;
251            let items_size: usize = self.iter().map(|item| item.serialized_size(compress)).sum();
252            len_size + items_size
253        }
254    }
255
256    impl<T: DoryDeserialize> DoryDeserialize for Vec<T> {
257        fn deserialize_with_mode<R: Read>(
258            mut reader: R,
259            compress: Compress,
260            validate: Validate,
261        ) -> Result<Self, SerializationError> {
262            let len = u64::deserialize_with_mode(&mut reader, compress, validate)? as usize;
263            let mut vec = Vec::with_capacity(len);
264            for _ in 0..len {
265                vec.push(T::deserialize_with_mode(&mut reader, compress, validate)?);
266            }
267            Ok(vec)
268        }
269    }
270}