ps_datachunk/serialized/
mod.rs1use std::ops::Deref;
2
3use bytes::Bytes;
4use ps_buffer::{Buffer, SharedBuffer};
5use ps_hash::{hash, Hash, HASH_SIZE};
6
7use crate::{DataChunk, DataChunkError, EncryptedDataChunk, Result};
8
9#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
10pub struct SerializedDataChunk {
11 buffer: Buffer,
12 hash: Hash,
13}
14
15impl SerializedDataChunk {
16 #[must_use]
17 pub const fn data_length(&self) -> usize {
18 self.buffer.len().saturating_sub(HASH_SIZE)
19 }
20
21 #[must_use]
22 pub const fn is_empty(&self) -> bool {
23 self.buffer.len() <= HASH_SIZE
24 }
25
26 pub fn from_parts_unchecked<D>(data: D, hash: Hash) -> Result<Self>
34 where
35 D: AsRef<[u8]>,
36 {
37 let data = data.as_ref();
38 let buffer_length = HASH_SIZE + data.len();
39
40 let mut buffer = Buffer::with_capacity(buffer_length)?;
41
42 buffer.extend_from_slice(hash.to_string())?;
43 buffer.extend_from_slice(data)?;
44
45 let chunk = Self { buffer, hash };
46
47 Ok(chunk)
48 }
49
50 pub fn from_data<D>(data: D) -> Result<Self>
52 where
53 D: AsRef<[u8]>,
54 {
55 let data = data.as_ref();
56
57 Self::from_parts_unchecked(data, hash(data)?)
58 }
59
60 #[inline]
62 #[must_use]
63 pub fn serialized_bytes(&self) -> &[u8] {
64 &self.buffer
65 }
66
67 pub fn from_serialized_buffer(buffer: Buffer) -> Result<Self> {
73 if buffer.len() < HASH_SIZE {
74 return Err(DataChunkError::InvalidLayout);
75 }
76
77 let hash = &buffer[..HASH_SIZE];
78 let data = &buffer[HASH_SIZE..];
79 let calculated_hash = ps_hash::hash(data)?;
80
81 if hash != calculated_hash.to_string().as_bytes() {
82 return Err(DataChunkError::HashMismatch);
83 }
84
85 let chunk = Self {
86 buffer,
87 hash: calculated_hash,
88 };
89
90 Ok(chunk)
91 }
92
93 #[inline]
94 pub fn into_buffer(self) -> Buffer {
96 self.buffer
97 }
98
99 #[inline]
100 pub fn into_parts(self) -> (Buffer, Hash) {
102 (self.buffer, self.hash)
103 }
104}
105
106impl DataChunk for SerializedDataChunk {
107 fn data_ref(&self) -> &[u8] {
108 &self.buffer[HASH_SIZE..]
109 }
110
111 fn encrypt(&self) -> Result<EncryptedDataChunk> {
112 Ok(ps_cypher::encrypt(&self.buffer)?.into())
113 }
114
115 fn hash_ref(&self) -> &Hash {
116 &self.hash
117 }
118
119 fn into_bytes(self) -> Bytes {
121 Bytes::from_owner(SharedBuffer::from(self.buffer)).slice(HASH_SIZE..)
122 }
123
124 fn into_owned(self) -> crate::OwnedDataChunk {
126 let hash = self.hash();
127
128 crate::OwnedDataChunk::from_data_and_hash_unchecked(self, hash)
129 }
130}
131
132impl AsRef<[u8]> for SerializedDataChunk {
133 fn as_ref(&self) -> &[u8] {
134 self
135 }
136}
137
138impl Deref for SerializedDataChunk {
139 type Target = [u8];
140
141 fn deref(&self) -> &Self::Target {
142 self.data_ref()
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::{DataChunk, OwnedDataChunk};
150
151 #[test]
152 fn from_parts_unchecked_accepts_matching_hash() -> Result<()> {
153 let data = b"hello world";
154 let hash = ps_hash::hash(data)?;
155
156 let chunk = SerializedDataChunk::from_parts_unchecked(data, hash)?;
157
158 assert_eq!(chunk.data_ref(), data);
159
160 Ok(())
161 }
162
163 #[test]
164 fn from_parts_unchecked_accepts_mismatched_hash() -> Result<()> {
165 let data = b"hello world";
166 let mismatched_hash = ps_hash::hash(b"other bytes")?;
167
168 let chunk = SerializedDataChunk::from_parts_unchecked(data, mismatched_hash)?;
169
170 assert_eq!(chunk.data_ref(), data);
171 assert_eq!(chunk.hash(), mismatched_hash);
172
173 Ok(())
174 }
175
176 #[test]
177 fn serialize_owned_chunk_roundtrip_data() -> Result<()> {
178 let original_data = vec![1, 2, 3, 4, 5];
179 let hash = ps_hash::hash(&original_data)?;
180 let data_chunk = OwnedDataChunk::from_data_and_hash_unchecked(original_data.clone(), hash);
181
182 let serialized = data_chunk.serialize()?;
183
184 assert_eq!(serialized.data_ref(), original_data);
185
186 Ok(())
187 }
188}