mod cache;
mod memoryblobstore;
pub mod encodings;
use crate::metadata::MetaDescribe;
use crate::inline::encodings::hash::Handle;
use crate::inline::Inline;
use crate::inline::InlineEncoding;
use std::convert::Infallible;
use std::error::Error;
use std::fmt::Debug;
use std::fmt::{self};
use std::hash::Hash;
use std::marker::PhantomData;
pub use cache::BlobCache;
pub use memoryblobstore::MemoryBlobStore;
pub use anybytes::Bytes;
pub struct Blob<S: BlobEncoding> {
pub bytes: Bytes,
handle: Inline<Handle<S>>,
_schema: PhantomData<S>,
}
impl<S> Blob<S>
where
S: BlobEncoding,
Handle<S>: InlineEncoding,
{
pub fn new(bytes: Bytes) -> Self {
let digest = crate::inline::encodings::hash::Blake3::digest(&bytes);
Self {
bytes,
handle: Inline::new(digest),
_schema: PhantomData,
}
}
pub fn with_handle(bytes: Bytes, handle: Inline<Handle<S>>) -> Self {
Self {
bytes,
handle,
_schema: PhantomData,
}
}
pub fn transmute<T: BlobEncoding>(self) -> Blob<T>
where
Handle<T>: InlineEncoding,
{
Blob {
bytes: self.bytes,
handle: self.handle.transmute(),
_schema: PhantomData,
}
}
pub fn as_transmute<T: BlobEncoding>(&self) -> &Blob<T> {
unsafe { std::mem::transmute(self) }
}
pub fn get_handle(&self) -> Inline<Handle<S>> {
self.handle
}
pub fn try_from_blob<T>(self) -> Result<T, <T as TryFromBlob<S>>::Error>
where
T: TryFromBlob<S>,
{
<T as TryFromBlob<S>>::try_from_blob(self)
}
}
impl<T> Clone for Blob<T>
where
T: BlobEncoding,
Handle<T>: InlineEncoding,
{
fn clone(&self) -> Self {
Self {
bytes: self.bytes.clone(),
handle: self.handle,
_schema: PhantomData,
}
}
}
impl<S> AsRef<Inline<Handle<S>>> for Blob<S>
where
S: BlobEncoding,
Handle<S>: InlineEncoding,
{
fn as_ref(&self) -> &Inline<Handle<S>> {
&self.handle
}
}
impl<T: BlobEncoding> PartialEq for Blob<T> {
fn eq(&self, other: &Self) -> bool {
self.bytes == other.bytes
}
}
impl<T: BlobEncoding> Eq for Blob<T> {}
impl<T: BlobEncoding> Hash for Blob<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}
impl<T: BlobEncoding> Debug for Blob<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Blob<{}>", std::any::type_name::<T>())
}
}
pub trait BlobEncoding: MetaDescribe + Sized + 'static {
fn blob_from<T: IntoBlob<Self>>(t: T) -> Blob<Self> {
t.to_blob()
}
fn to_encoded(blob: Blob<Self>) -> crate::inline::Encoded<Handle<Self>>
where
Handle<Self>: InlineEncoding,
{
crate::inline::Encoded::Blob(blob.transmute::<crate::blob::encodings::UnknownBlob>())
}
}
pub trait IntoBlob<S: BlobEncoding>:
crate::inline::IntoEncoded<S, Output = Blob<S>>
{
fn to_blob(self) -> Blob<S>
where
Self: Sized,
{
self.into_encoded()
}
}
impl<S, T> IntoBlob<S> for T
where
S: BlobEncoding,
T: crate::inline::IntoEncoded<S, Output = Blob<S>>,
{
}
pub trait TryFromBlob<S: BlobEncoding>: Sized {
type Error: Error + Send + Sync + 'static;
fn try_from_blob(b: Blob<S>) -> Result<Self, Self::Error>;
}
impl<S: BlobEncoding> TryFromBlob<S> for Blob<S> {
type Error = Infallible;
fn try_from_blob(b: Blob<S>) -> Result<Self, Self::Error> {
Ok(b)
}
}
impl<S: BlobEncoding> crate::inline::Encodes<Blob<S>> for S
where
Handle<S>: InlineEncoding,
{
type Output = Blob<S>;
fn encode(source: Blob<S>) -> Blob<S> {
source
}
}
impl<T> crate::inline::ToEncoded<Handle<T>> for Blob<T>
where
T: BlobEncoding,
Handle<T>: InlineEncoding,
{
fn to_encoded(self) -> crate::inline::Encoded<Handle<T>> {
<T as BlobEncoding>::to_encoded(self)
}
}
impl<T: BlobEncoding> crate::inline::Encodes<Inline<Handle<T>>> for T
where
Handle<T>: InlineEncoding,
{
type Output = Inline<Handle<T>>;
fn encode(source: Inline<Handle<T>>) -> Inline<Handle<T>> {
source
}
}
impl<T: BlobEncoding> crate::inline::Encodes<&Inline<Handle<T>>> for T
where
Handle<T>: InlineEncoding,
{
type Output = Inline<Handle<T>>;
fn encode(source: &Inline<Handle<T>>) -> Inline<Handle<T>> {
*source
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::blob::encodings::UnknownBlob;
use crate::inline::encodings::hash::Blake3;
#[test]
fn new_computes_and_caches_handle() {
let b: Blob<UnknownBlob> = Blob::new(Bytes::from(b"hello".to_vec()));
let h1 = b.get_handle();
let h2 = b.get_handle();
assert_eq!(h1, h2);
let independent = Inline::new(Blake3::digest(b"hello"));
let h_typed: Inline<Handle<UnknownBlob>> = independent;
assert_eq!(h1, h_typed);
}
#[test]
fn with_handle_trusts_the_provided_handle() {
let bogus: Inline<Handle<UnknownBlob>> = Inline::new([0xAA; 32]);
let b: Blob<UnknownBlob> = Blob::with_handle(
Bytes::from(b"any bytes".to_vec()),
bogus,
);
assert_eq!(b.get_handle(), bogus);
}
#[test]
fn as_ref_borrows_the_lightweight_handle() {
let b: Blob<UnknownBlob> = Blob::new(Bytes::from(b"borrow me".to_vec()));
let h_owned: Inline<Handle<UnknownBlob>> = b.get_handle();
let h_borrowed: &Inline<Handle<UnknownBlob>> = b.as_ref();
assert_eq!(h_owned, *h_borrowed);
}
#[test]
fn transmute_carries_cached_handle() {
let b: Blob<UnknownBlob> = Blob::new(Bytes::from(b"shared".to_vec()));
let h_before: Inline<Handle<UnknownBlob>> = b.get_handle();
let b2: Blob<crate::blob::encodings::longstring::LongString> =
b.transmute::<crate::blob::encodings::longstring::LongString>();
let h_after = b2.get_handle();
assert_eq!(h_before.raw, h_after.raw);
}
}