Skip to main content

ps_datachunk/
lib.rs

1#![allow(clippy::missing_errors_doc)]
2#![allow(clippy::module_name_repetitions)]
3pub mod aligned;
4pub mod borrowed;
5pub mod cow;
6pub mod encrypted;
7pub mod error;
8pub mod mbuf;
9pub mod owned;
10pub mod serialized;
11pub mod typed;
12pub mod utils;
13pub use aligned::AlignedDataChunk;
14pub use borrowed::BorrowedDataChunk;
15pub use bytes::Bytes;
16pub use cow::CowDataChunk;
17pub use encrypted::EncryptedDataChunk;
18pub use error::DataChunkError;
19pub use error::Result;
20pub use mbuf::MbufDataChunk;
21pub use owned::OwnedDataChunk;
22pub use ps_hash::Hash;
23pub use ps_mbuf::Mbuf;
24pub use serialized::SerializedDataChunk;
25pub use typed::ToDataChunk;
26pub use typed::ToTypedDataChunk;
27pub use typed::TypedDataChunk;
28
29use std::sync::Arc;
30
31/// Represents any representation of a chunk of data.
32///
33/// # Invariants
34///
35/// Implementers must treat the bytes and hash exposed through `&self` as immutable and stable.
36/// In practice this means:
37/// - Repeated calls to [`Self::data_ref`] must point to the same logical bytes while `self` is borrowed.
38/// - Repeated calls to [`Self::hash_ref`] must return the hash for those same bytes.
39/// - Neither value may change through interior mutability while `self` is borrowed.
40///
41/// `TypedDataChunk` relies on this contract for its unchecked deref fast path.
42pub trait DataChunk
43where
44    Self: Sized,
45{
46    /// Returns a stable view of the underlying bytes.
47    fn data_ref(&self) -> &[u8];
48    /// Returns a stable view of the hash corresponding to [`Self::data_ref`].
49    fn hash_ref(&self) -> &Hash;
50
51    fn hash(&self) -> Hash {
52        *self.hash_ref()
53    }
54
55    fn encrypt(&self) -> Result<EncryptedDataChunk> {
56        self.serialize()?.encrypt()
57    }
58
59    fn decrypt(&self, key: &Hash) -> Result<SerializedDataChunk> {
60        utils::decrypt(self.data_ref(), key)
61    }
62
63    fn borrow(&self) -> BorrowedDataChunk<'_> {
64        BorrowedDataChunk::from_parts_unchecked(self.data_ref(), self.hash())
65    }
66
67    fn serialize(&self) -> Result<SerializedDataChunk> {
68        SerializedDataChunk::from_parts_unchecked(self.data_ref(), self.hash())
69    }
70
71    /// Transforms this [`DataChunk`] into [`Bytes`].
72    fn into_bytes(self) -> Bytes {
73        Bytes::from_owner(Arc::from(self.data_ref()))
74    }
75
76    /// Copies this [`DataChunk`] into a new [`OwnedDataChunk`].
77    fn into_owned(self) -> OwnedDataChunk {
78        OwnedDataChunk::from_data_and_hash_unchecked(Arc::from(self.data_ref()), self.hash())
79    }
80
81    fn try_as<T: rkyv::Archive>(self) -> Result<TypedDataChunk<Self, T>>
82    where
83        T::Archived:
84            for<'a> rkyv::bytecheck::CheckBytes<rkyv::api::high::HighValidator<'a, rancor::Error>>,
85    {
86        TypedDataChunk::<Self, T>::from_data_chunk(self)
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    // Keep crate-root tests for cross-module flow behavior.
95    #[test]
96    fn test_encryption_decryption() -> Result<()> {
97        let original_data = "Neboť tak Bůh miluje svět, že dal [svého] jediného Syna, aby žádný, kdo v něho věří, nezahynul, ale měl život věčný. Vždyť Bůh neposlal [svého] Syna na svět, aby svět odsoudil, ale aby byl svět skrze něj zachráněn.".as_bytes().to_owned();
98
99        let data_chunk = BorrowedDataChunk::from_data(&original_data)?;
100
101        let encrypted_chunk = data_chunk.encrypt()?;
102        let decrypted_chunk = encrypted_chunk.decrypt()?;
103
104        assert_eq!(decrypted_chunk.data_ref(), original_data);
105
106        Ok(())
107    }
108}