ouisync/protocol/
block.rs1use crate::crypto::{Digest, Hash, Hashable};
2use rand::{distributions::Standard, prelude::Distribution, Rng};
3use serde::{Deserialize, Serialize};
4use std::{
5 array::TryFromSliceError,
6 fmt,
7 ops::{Deref, DerefMut},
8};
9use zeroize::Zeroize;
10
11pub const BLOCK_SIZE: usize = 32 * 1024;
13
14pub const BLOCK_RECORD_SIZE: u64 =
16 BLOCK_SIZE as u64 + BlockId::SIZE as u64 + BLOCK_NONCE_SIZE as u64;
17
18pub(crate) const BLOCK_NONCE_SIZE: usize = 32;
19pub(crate) type BlockNonce = [u8; BLOCK_NONCE_SIZE];
20
21#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
23#[repr(transparent)]
24pub struct BlockId(Hash);
25
26impl BlockId {
27 pub(crate) const SIZE: usize = Hash::SIZE;
28
29 pub(crate) fn new(content: &BlockContent, nonce: &BlockNonce) -> Self {
31 Self((&content[..], &nonce[..]).hash())
32 }
33}
34
35impl AsRef<[u8]> for BlockId {
36 fn as_ref(&self) -> &[u8] {
37 self.0.as_ref()
38 }
39}
40
41impl TryFrom<&'_ [u8]> for BlockId {
42 type Error = TryFromSliceError;
43
44 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
45 Hash::try_from(slice).map(Self)
46 }
47}
48
49impl fmt::Display for BlockId {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 self.0.fmt(f)
52 }
53}
54
55impl fmt::Debug for BlockId {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 self.0.fmt(f)
58 }
59}
60
61impl Hashable for BlockId {
62 fn update_hash<S: Digest>(&self, state: &mut S) {
63 self.0.update_hash(state)
64 }
65}
66
67derive_sqlx_traits_for_byte_array_wrapper!(BlockId);
68
69#[cfg(test)]
70derive_rand_for_wrapper!(BlockId);
71
72#[derive(Clone)]
73pub(crate) struct Block {
74 pub id: BlockId,
75 pub content: BlockContent,
76 pub nonce: BlockNonce,
77}
78
79impl Block {
80 pub fn new(content: BlockContent, nonce: BlockNonce) -> Self {
81 let id = BlockId::new(&content, &nonce);
82 Self { id, content, nonce }
83 }
84}
85
86impl Distribution<Block> for Standard {
87 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Block {
88 let content = rng.r#gen();
89 let nonce = rng.r#gen();
90 Block::new(content, nonce)
91 }
92}
93
94impl fmt::Debug for Block {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 f.debug_struct("Block")
97 .field("id", &self.id)
98 .finish_non_exhaustive()
99 }
100}
101
102#[derive(Clone, Serialize, Deserialize)]
103pub(crate) struct BlockContent(Box<[u8]>);
104
105impl BlockContent {
106 pub fn new() -> Self {
107 Self::default()
108 }
109
110 pub fn read_array<const N: usize>(&self, offset: usize) -> [u8; N] {
116 self[offset..offset + N].try_into().unwrap()
117 }
118
119 pub fn read_u64(&self, offset: usize) -> u64 {
125 u64::from_le_bytes(self.read_array(offset))
126 }
127
128 pub fn read(&self, offset: usize, dst: &mut [u8]) {
130 dst.copy_from_slice(&self.0[offset..offset + dst.len()]);
131 }
132
133 pub fn write_u64(&mut self, offset: usize, value: u64) {
135 let bytes = value.to_le_bytes();
136 self.write(offset, &bytes[..]);
137 }
138
139 pub fn write(&mut self, offset: usize, src: &[u8]) {
141 self.0[offset..offset + src.len()].copy_from_slice(src);
142 }
143}
144
145impl Default for BlockContent {
146 fn default() -> Self {
147 Self(vec![0; BLOCK_SIZE].into_boxed_slice())
148 }
149}
150
151impl Drop for BlockContent {
154 fn drop(&mut self) {
155 self.0.zeroize()
156 }
157}
158
159impl Deref for BlockContent {
160 type Target = [u8];
161
162 fn deref(&self) -> &Self::Target {
163 &self.0
164 }
165}
166
167impl DerefMut for BlockContent {
168 fn deref_mut(&mut self) -> &mut Self::Target {
169 &mut self.0
170 }
171}
172
173impl Distribution<BlockContent> for Standard {
174 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> BlockContent {
175 let mut content = vec![0; BLOCK_SIZE].into_boxed_slice();
176 rng.fill(&mut content[..]);
177
178 BlockContent(content)
179 }
180}
181
182impl fmt::Debug for BlockContent {
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184 write!(f, "{:<8}", hex_fmt::HexFmt(&self[..]))
185 }
186}
187
188#[cfg(test)]
189mod test_utils {
190 use super::{Block, BlockContent, BlockNonce, BLOCK_SIZE};
191 use proptest::{
192 arbitrary::{any, Arbitrary, StrategyFor},
193 collection::{vec, VecStrategy},
194 strategy::{Map, NoShrink, Strategy},
195 };
196
197 impl Arbitrary for BlockContent {
198 type Parameters = ();
199 type Strategy = Map<NoShrink<VecStrategy<StrategyFor<u8>>>, fn(Vec<u8>) -> Self>;
200
201 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
202 vec(any::<u8>(), BLOCK_SIZE)
203 .no_shrink()
204 .prop_map(|bytes| Self(bytes.into_boxed_slice()))
205 }
206 }
207
208 impl Arbitrary for Block {
209 type Parameters = ();
210 type Strategy = Map<
211 (StrategyFor<BlockContent>, NoShrink<StrategyFor<BlockNonce>>),
212 fn((BlockContent, BlockNonce)) -> Self,
213 >;
214
215 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
216 (any::<BlockContent>(), any::<BlockNonce>().no_shrink())
217 .prop_map(|(content, nonce)| Self::new(content, nonce))
218 }
219 }
220}