Skip to main content

triblespace_core/
blob.rs

1//! Anything that can be represented as a byte sequence.
2//!
3//! Blobs store larger data items outside tribles and values. For the design
4//! rationale and an extended usage example see the [Blobs
5//! chapter](../book/src/deep-dive/blobs.md) of the Tribles Book.
6
7// Converting Rust types to blobs is infallible in practice, so only `ToBlob`
8// and `TryFromBlob` are used throughout the codebase.  `TryToBlob` and
9// `FromBlob` were never required and have been removed for simplicity.
10
11mod cache;
12mod memoryblobstore;
13/// Built-in blob schema types and their conversion implementations.
14pub mod schemas;
15
16use crate::metadata::ConstId;
17use crate::value::schemas::hash::Handle;
18use crate::value::schemas::hash::HashProtocol;
19use crate::value::Value;
20use crate::value::ValueSchema;
21
22use std::convert::Infallible;
23use std::error::Error;
24use std::fmt::Debug;
25use std::fmt::{self};
26use std::hash::Hash;
27use std::marker::PhantomData;
28
29/// Re-export of the blob cache wrapper.
30pub use cache::BlobCache;
31/// Re-export of the in-memory blob store.
32pub use memoryblobstore::MemoryBlobStore;
33
34/// Re-export of `anybytes::Bytes` for blob payloads.
35pub use anybytes::Bytes;
36
37/// A blob is a immutable sequence of bytes that can be used to represent any kind of data.
38/// It is the fundamental building block of data storage and transmission.
39/// The [`BlobSchema`] type parameter is used to define the abstract schema type of a blob.
40/// This is similar to the [`Value`] type and the [`ValueSchema`] trait in the [`value`](crate::value) module.
41#[repr(transparent)]
42pub struct Blob<S: BlobSchema> {
43    /// The raw byte content of this blob.
44    pub bytes: Bytes,
45    _schema: PhantomData<S>,
46}
47
48impl<S: BlobSchema> Blob<S> {
49    /// Creates a new blob from a sequence of bytes.
50    /// The bytes are stored in the blob as-is.
51    pub fn new(bytes: Bytes) -> Self {
52        Self {
53            bytes,
54            _schema: PhantomData,
55        }
56    }
57
58    /// Reinterprets the contained bytes as a blob of a different schema.
59    ///
60    /// This is a zero-copy transformation that simply changes the compile-time
61    /// schema marker. It does **not** validate that the data actually conforms
62    /// to the new schema.
63    pub fn transmute<T: BlobSchema>(self) -> Blob<T> {
64        Blob {
65            bytes: self.bytes,
66            _schema: PhantomData,
67        }
68    }
69
70    /// Transmutes the blob to a blob of a different schema.
71    /// This is a zero-cost operation.
72    /// If the schema types are not compatible, this will not cause undefined behavior,
73    /// but it might cause unexpected results.
74    ///
75    /// This is primarily used to give blobs with an [UnknownBlob](crate::blob::schemas::UnknownBlob) schema a more specific schema.
76    /// Use with caution.
77    pub fn as_transmute<T: BlobSchema>(&self) -> &Blob<T> {
78        unsafe { std::mem::transmute(self) }
79    }
80
81    // Note: Do we want to cache the handle somewhere so that we don't have to compute the hash every time?
82    // We could use WeakBytes for this, but it would require one hash-map per HashProtocol.
83
84    /// Hashes the blob with the given hash protocol and returns the hash as a handle.
85    pub fn get_handle<H>(&self) -> Value<Handle<H, S>>
86    where
87        H: HashProtocol,
88        Handle<H, S>: ValueSchema,
89    {
90        let digest = H::digest(&self.bytes);
91        Value::new(digest.into())
92    }
93
94    /// Tries to convert the blob to a concrete Rust type.
95    /// If the conversion fails, an error is returned.
96    pub fn try_from_blob<T>(self) -> Result<T, <T as TryFromBlob<S>>::Error>
97    where
98        T: TryFromBlob<S>,
99    {
100        <T as TryFromBlob<S>>::try_from_blob(self)
101    }
102}
103
104impl<T: BlobSchema> Clone for Blob<T> {
105    fn clone(&self) -> Self {
106        Self {
107            bytes: self.bytes.clone(),
108            _schema: PhantomData,
109        }
110    }
111}
112
113impl<T: BlobSchema> PartialEq for Blob<T> {
114    fn eq(&self, other: &Self) -> bool {
115        self.bytes == other.bytes
116    }
117}
118
119impl<T: BlobSchema> Eq for Blob<T> {}
120
121impl<T: BlobSchema> Hash for Blob<T> {
122    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
123        self.bytes.hash(state);
124    }
125}
126
127impl<T: BlobSchema> Debug for Blob<T> {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        write!(f, "Blob<{}>", std::any::type_name::<T>())
130    }
131}
132
133/// A trait for defining the abstract schema type of a blob.
134/// This is similar to the [`ValueSchema`] trait in the [`value`](crate::value) module.
135pub trait BlobSchema: ConstId + Sized + 'static {
136    /// Converts a concrete Rust type to a blob with this schema.
137    /// If the conversion fails, this might cause a panic.
138    fn blob_from<T: ToBlob<Self>>(t: T) -> Blob<Self> {
139        t.to_blob()
140    }
141}
142
143/// A trait for converting a Rust type to a [Blob] with a specific schema.
144/// This trait is implemented on the concrete Rust type.
145///
146/// Conversions are infallible.  Use [`TryFromBlob`] on the target type to
147/// perform the fallible reverse conversion.
148///
149/// See [ToValue](crate::value::ToValue) for the counterpart trait for values.
150pub trait ToBlob<S: BlobSchema> {
151    /// Converts this value into a blob.
152    fn to_blob(self) -> Blob<S>;
153}
154
155/// A trait for converting a [Blob] with a specific schema to a Rust type.
156/// This trait is implemented on the concrete Rust type.
157///
158/// This might return an error if the conversion is not possible,
159/// This is the counterpart to the [`ToBlob`] trait.
160///
161/// See [TryFromValue](crate::value::TryFromValue) for the counterpart trait for values.
162pub trait TryFromBlob<S: BlobSchema>: Sized {
163    /// The error type returned when the conversion fails.
164    type Error: Error + Send + Sync + 'static;
165    /// Attempts to convert a blob into this type.
166    fn try_from_blob(b: Blob<S>) -> Result<Self, Self::Error>;
167}
168
169impl<S: BlobSchema> TryFromBlob<S> for Blob<S> {
170    type Error = Infallible;
171
172    fn try_from_blob(b: Blob<S>) -> Result<Self, Self::Error> {
173        Ok(b)
174    }
175}
176
177impl<S: BlobSchema> ToBlob<S> for Blob<S> {
178    fn to_blob(self) -> Blob<S> {
179        self
180    }
181}