ps_datachunk/serialized/
mod.rs

1use std::{ops::Deref, sync::Arc};
2
3use ps_buffer::Buffer;
4use ps_hash::{hash, Hash};
5
6use crate::{utils::HASH_SIZE, DataChunk, EncryptedDataChunk, PsDataChunkError, Result};
7
8#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
9pub struct SerializedDataChunk {
10    buffer: Buffer,
11    hash: Arc<Hash>,
12}
13
14impl SerializedDataChunk {
15    #[must_use]
16    pub const fn data_length(&self) -> usize {
17        self.buffer.len().saturating_sub(HASH_SIZE)
18    }
19
20    #[must_use]
21    pub const fn is_empty(&self) -> bool {
22        self.buffer.len() <= HASH_SIZE
23    }
24
25    /// # Safety
26    ///
27    /// Called guarantees that `hash` is `hash(data)`
28    ///
29    /// This method does **NOT** verify `hash`!
30    ///
31    /// Call only if `hash` is surely known.
32    pub fn from_parts<D>(data: D, hash: Arc<Hash>) -> Result<Self>
33    where
34        D: AsRef<[u8]>,
35    {
36        let data = data.as_ref();
37        let buffer_length = HASH_SIZE + data.len();
38
39        let mut buffer = Buffer::with_capacity(buffer_length)?;
40
41        buffer.extend_from_slice(hash.as_bytes())?;
42        buffer.extend_from_slice(data)?;
43
44        let chunk = Self { buffer, hash };
45
46        Ok(chunk)
47    }
48
49    /// # Safety
50    ///
51    /// Called guarantees that `hash` is `hash(data)`
52    ///
53    /// This method does **NOT** verify `hash`!
54    ///
55    /// This method only verifies the internal checksum of `hash`, and will return `Err(PsDataChunkError::InvalidChecksum)` if this is invalid.
56    pub fn try_from_parts<D, H>(data: D, hash: H) -> Result<Self>
57    where
58        D: AsRef<[u8]>,
59        H: AsRef<[u8]>,
60    {
61        let data = data.as_ref();
62        let hash = hash.as_ref();
63
64        let hash = Hash::try_from(hash)?.into();
65
66        Self::from_parts(data, hash)
67    }
68
69    /// Allocate a `SerializedDataChunk` containing `data`
70    pub fn from_data<D>(data: D) -> Result<Self>
71    where
72        D: AsRef<[u8]>,
73    {
74        let data = data.as_ref();
75
76        Self::from_parts(data, hash(data)?.into())
77    }
78
79    /// Returns a reference to this [`SerializedDataChunk`]'s serialized bytes
80    #[inline]
81    #[must_use]
82    pub fn serialized_bytes(&self) -> &[u8] {
83        &self.buffer
84    }
85
86    /// Constructs a `SerializedDataChunk` from a serialized buffer.
87    ///
88    /// `buffer` is validated to be interpretable as a `SerializedDataChunk`,
89    /// and its `hash` is recalculated and verified. However, other things,
90    /// such as padding and buffer length, are not validated.
91    pub fn from_serialized_buffer(buffer: Buffer) -> Result<Self> {
92        if buffer.len() < HASH_SIZE {
93            return Err(PsDataChunkError::InvalidDataChunk);
94        }
95
96        let hash = &buffer[..HASH_SIZE];
97        let data = &buffer[HASH_SIZE..];
98        let calculated_hash = ps_hash::hash(data)?;
99
100        if hash != calculated_hash.as_bytes() {
101            return Err(PsDataChunkError::InvalidHash);
102        }
103
104        let chunk = Self {
105            buffer,
106            hash: Arc::from(calculated_hash),
107        };
108
109        Ok(chunk)
110    }
111
112    #[inline]
113    /// extracts the serialized `Buffer` from this `SerializedDataChunk`
114    pub fn into_buffer(self) -> Buffer {
115        self.buffer
116    }
117
118    #[inline]
119    /// extracts the serialized `Buffer` and `Hash` from this `SerializedDataChunk`
120    pub fn into_parts(self) -> (Buffer, Arc<Hash>) {
121        (self.buffer, self.hash)
122    }
123}
124
125impl DataChunk for SerializedDataChunk {
126    fn data_ref(&self) -> &[u8] {
127        &self.buffer[HASH_SIZE..]
128    }
129
130    fn encrypt(&self) -> Result<EncryptedDataChunk> {
131        Ok(ps_cypher::encrypt(&self.buffer)?.into())
132    }
133
134    fn hash_ref(&self) -> &Hash {
135        &self.hash
136    }
137
138    fn hash(&self) -> Arc<Hash> {
139        self.hash.clone()
140    }
141
142    /// Transforms this chunk into an [`OwnedDataChunk`]
143    fn into_owned(self) -> crate::OwnedDataChunk {
144        let hash = self.hash();
145
146        crate::OwnedDataChunk::from_data_and_hash(self, hash)
147    }
148}
149
150impl AsRef<[u8]> for SerializedDataChunk {
151    fn as_ref(&self) -> &[u8] {
152        self
153    }
154}
155
156impl Deref for SerializedDataChunk {
157    type Target = [u8];
158
159    fn deref(&self) -> &Self::Target {
160        self.data_ref()
161    }
162}