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}