ps_datachunk/
aligned.rs

1use std::ops::Deref;
2
3use ps_hash::hash;
4use rancor::{Error, Strategy};
5use rkyv::{
6    api::high::HighSerializer,
7    bytecheck::CheckBytes,
8    ser::allocator::ArenaHandle,
9    util::AlignedVec,
10    validation::{archive::ArchiveValidator, shared::SharedValidator, Validator},
11    Archive, Serialize,
12};
13
14use crate::*;
15
16#[derive(Debug, Clone)]
17pub struct AlignedDataChunk {
18    data: AlignedVec,
19    hash: Arc<Hash>,
20}
21
22impl AlignedDataChunk {
23    pub fn from_parts<D, H>(data: D, hash: H) -> Self
24    where
25        D: Into<AlignedVec>,
26        H: Into<Arc<Hash>>,
27    {
28        let data = data.into();
29        let hash = hash.into();
30
31        Self { data, hash }
32    }
33
34    pub fn from_data_vec(data: AlignedVec) -> Self {
35        let hash = hash(&data);
36
37        Self::from_parts(data, hash)
38    }
39
40    pub fn is_empty(&self) -> bool {
41        self.data.is_empty()
42    }
43
44    pub fn len(&self) -> usize {
45        self.data.len()
46    }
47}
48
49impl AsRef<[u8]> for AlignedDataChunk {
50    fn as_ref(&self) -> &[u8] {
51        self
52    }
53}
54
55impl Deref for AlignedDataChunk {
56    type Target = [u8];
57
58    fn deref(&self) -> &Self::Target {
59        &self.data
60    }
61}
62
63impl AlignedDataChunk {
64    pub fn try_from<T>(value: &T) -> Result<Self>
65    where
66        for<'a> T: Archive + Serialize<HighSerializer<AlignedVec, ArenaHandle<'a>, Error>>,
67    {
68        let data = rkyv::to_bytes::<Error>(value)?;
69        let chunk = Self::from_data_vec(data);
70
71        Ok(chunk)
72    }
73
74    pub fn try_bytes_as<T: rkyv::Archive>(data: &[u8]) -> Result<&T::Archived>
75    where
76        for<'a> <T as rkyv::Archive>::Archived:
77            CheckBytes<Strategy<Validator<ArchiveValidator<'a>, SharedValidator>, rancor::Error>>,
78    {
79        Ok(rkyv::access::<T::Archived, Error>(data)?)
80    }
81
82    pub fn try_as<T: rkyv::Archive>(&self) -> Result<&T::Archived>
83    where
84        for<'a> <T as Archive>::Archived:
85            CheckBytes<Strategy<Validator<ArchiveValidator<'a>, SharedValidator>, rancor::Error>>,
86    {
87        Self::try_bytes_as::<T>(self.data_ref())
88    }
89}
90
91impl DataChunkTrait for AlignedDataChunk {
92    fn data_ref(&self) -> &[u8] {
93        self
94    }
95    fn hash_ref(&self) -> &[u8] {
96        self.hash.as_bytes()
97    }
98    fn hash(&self) -> Arc<Hash> {
99        self.hash.clone()
100    }
101}
102
103impl<'lt> DataChunk<'lt> {
104    pub fn try_from<T>(value: &T) -> Result<Self>
105    where
106        T: Archive + for<'a> Serialize<HighSerializer<AlignedVec, ArenaHandle<'a>, Error>>,
107    {
108        Ok(Self::Aligned(AlignedDataChunk::try_from(value)?))
109    }
110
111    pub fn try_as<T: rkyv::Archive>(&'lt self) -> Result<&'lt T::Archived>
112    where
113        for<'a> <T as Archive>::Archived:
114            CheckBytes<Strategy<Validator<ArchiveValidator<'a>, SharedValidator>, rancor::Error>>,
115    {
116        AlignedDataChunk::try_bytes_as::<T>(self.data_ref())
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use utils::offsets;
123
124    use super::*;
125
126    #[test]
127    fn test_chunk_length_divisibility_and_part_alignment() -> Result<()> {
128        for i in 12..256 {
129            let data = (vec![i as u8; i], ());
130            let chunk = AlignedDataChunk::try_from::<_>(&data)?;
131
132            assert_eq!(chunk.serialize()?.serialized_bytes().len() % 16, 0);
133
134            let (hash_offset, size_offset, size) = offsets(i);
135
136            assert_eq!(hash_offset % 16, 0);
137            assert_eq!(size_offset % 8, 0);
138            assert_eq!(size % 16, 0);
139        }
140
141        Ok(())
142    }
143}