#![allow(clippy::type_complexity)]
pub mod async_store;
pub(crate) mod read_attempt;
pub use read_attempt::ReadFailure;
pub mod branch;
pub mod commit;
pub mod hybridstore;
pub mod memoryrepo;
#[cfg(feature = "object-store")]
pub mod objectstore;
pub mod pile;
pub mod proof;
pub use crate::collection::observed_store::ObservedStore;
pub use proof::{CapabilityProofRead, CapabilityProofStore};
pub mod yard;
#[derive(Clone, Copy)]
pub struct MissingBlob {
pub handle: Inline<Handle<UnknownBlob>>,
}
impl std::fmt::Display for MissingBlob {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"blob blake3:{} is not resident in this snapshot",
hex::encode(self.handle.raw)
)
}
}
impl std::error::Error for MissingBlob {}
pub fn is_missing_blob(mut error: &(dyn std::error::Error + 'static)) -> bool {
loop {
if error.is::<MissingBlob>() {
return true;
}
match error.source() {
Some(source) => error = source,
None => return false,
}
}
}
impl std::fmt::Debug for MissingBlob {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
pub trait StorageClose {
type Error: std::error::Error;
fn close(self) -> Result<(), Self::Error>;
}
pub trait StorageFlush {
type Error: std::error::Error + Send + Sync + 'static;
fn flush(&mut self) -> Result<(), Self::Error>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct StoreChanges(u8);
impl StoreChanges {
pub const NONE: Self = Self(0);
pub const BLOBS: Self = Self(1 << 0);
pub const COLLECTION_RECORDS: Self = Self(1 << 1);
pub const CAPABILITY_PROOFS: Self = Self(1 << 2);
pub const WANTS: Self = Self(1 << 3);
pub const ALL: Self = Self(
Self::BLOBS.0 | Self::COLLECTION_RECORDS.0 | Self::CAPABILITY_PROOFS.0 | Self::WANTS.0,
);
pub const fn contains(self, change: Self) -> bool {
self.0 & change.0 == change.0
}
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct StoreDependencies {
pub records: BTreeSet<CollectionRecordSelector>,
pub blobs: BTreeSet<CollectionData>,
pub capability_proofs: bool,
pub all_records: bool,
pub all_blobs: bool,
}
impl StoreDependencies {
pub fn is_empty(&self) -> bool {
self.records.is_empty()
&& self.blobs.is_empty()
&& !self.capability_proofs
&& !self.all_records
&& !self.all_blobs
}
}
pub trait StoreSnapshot: Clone + Send + Sync + 'static {
fn changes_since(&self, _previous: &Self) -> StoreChanges {
StoreChanges::ALL
}
fn changes_for(&self, previous: &Self, dependencies: &StoreDependencies) -> StoreChanges {
if dependencies.is_empty() {
return StoreChanges::NONE;
}
let observed = self.changes_since(previous);
let mut relevant = StoreChanges::NONE;
if (dependencies.all_records || !dependencies.records.is_empty())
&& observed.contains(StoreChanges::COLLECTION_RECORDS)
{
relevant = relevant.union(StoreChanges::COLLECTION_RECORDS);
}
if (dependencies.all_blobs || !dependencies.blobs.is_empty())
&& observed.contains(StoreChanges::BLOBS)
{
relevant = relevant.union(StoreChanges::BLOBS);
}
if dependencies.capability_proofs && observed.contains(StoreChanges::CAPABILITY_PROOFS) {
relevant = relevant.union(StoreChanges::CAPABILITY_PROOFS);
}
relevant
}
}
#[cfg(test)]
mod missing_blob_tests {
use super::*;
#[test]
fn missing_blob_names_the_handle_it_wanted() {
let raw = [0xABu8; INLINE_LEN];
let missing = MissingBlob {
handle: Inline::<Handle<UnknownBlob>>::new(raw),
};
let shown = missing.to_string();
assert!(
shown.contains(&hex::encode(raw)),
"the handle must appear in the message, got {shown:?}"
);
assert!(
shown.contains("blake3:"),
"the handle must carry its scheme, got {shown:?}"
);
assert_eq!(format!("{missing:?}"), shown);
}
}
#[cfg(test)]
mod store_dependency_tests {
use super::*;
use crate::collection::CollectionHandle;
#[derive(Clone)]
struct UnclassifiedSnapshot;
impl StoreSnapshot for UnclassifiedSnapshot {}
#[derive(Clone)]
struct ClassifiedSnapshot(StoreChanges);
impl StoreSnapshot for ClassifiedSnapshot {
fn changes_since(&self, _previous: &Self) -> StoreChanges {
self.0
}
}
#[test]
fn scoped_default_keeps_unclassified_wrappers_conservative() {
let snapshot = UnclassifiedSnapshot;
let mut dependencies = StoreDependencies::default();
assert!(dependencies.is_empty());
assert_eq!(
snapshot.changes_for(&snapshot, &dependencies),
StoreChanges::NONE
);
dependencies
.records
.insert(CollectionRecordSelector::Collection(CollectionHandle::new(
[1; INLINE_LEN],
)));
assert!(!dependencies.is_empty());
assert_eq!(
snapshot.changes_for(&snapshot, &dependencies),
StoreChanges::COLLECTION_RECORDS
);
dependencies
.blobs
.insert(CollectionData::new([2; INLINE_LEN]));
dependencies.capability_proofs = true;
let all_reads = StoreChanges::COLLECTION_RECORDS
.union(StoreChanges::BLOBS)
.union(StoreChanges::CAPABILITY_PROOFS);
assert_eq!(snapshot.changes_for(&snapshot, &dependencies), all_reads);
assert!(!all_reads.contains(StoreChanges::WANTS));
dependencies.records.clear();
dependencies.blobs.clear();
dependencies.all_records = true;
dependencies.all_blobs = true;
assert_eq!(snapshot.changes_for(&snapshot, &dependencies), all_reads);
}
#[test]
fn scoped_default_filters_only_uninterested_components() {
let before = ClassifiedSnapshot(StoreChanges::NONE);
let mut dependencies = StoreDependencies {
all_records: true,
..StoreDependencies::default()
};
for observed in [StoreChanges::NONE, StoreChanges::BLOBS, StoreChanges::WANTS] {
assert_eq!(
ClassifiedSnapshot(observed).changes_for(&before, &dependencies),
StoreChanges::NONE
);
}
assert_eq!(
ClassifiedSnapshot(StoreChanges::ALL).changes_for(&before, &dependencies),
StoreChanges::COLLECTION_RECORDS
);
dependencies.capability_proofs = true;
assert_eq!(
ClassifiedSnapshot(StoreChanges::CAPABILITY_PROOFS).changes_for(&before, &dependencies),
StoreChanges::CAPABILITY_PROOFS
);
dependencies
.blobs
.insert(CollectionData::new([3; INLINE_LEN]));
assert_eq!(
ClassifiedSnapshot(StoreChanges::BLOBS).changes_for(&before, &dependencies),
StoreChanges::BLOBS
);
}
}
pub trait SnapshotSource {
type Snapshot: StoreSnapshot;
type SnapshotError: Error + Debug + Send + Sync + 'static;
fn snapshot(&mut self) -> Result<Self::Snapshot, Self::SnapshotError>;
}
pub type SnapshotOf<S> = <S as SnapshotSource>::Snapshot;
pub type SnapshotErrorOf<S> = <S as SnapshotSource>::SnapshotError;
impl<S> SnapshotSource for &mut S
where
S: SnapshotSource + ?Sized,
{
type Snapshot = S::Snapshot;
type SnapshotError = S::SnapshotError;
fn snapshot(&mut self) -> Result<Self::Snapshot, Self::SnapshotError> {
(**self).snapshot()
}
}
impl<S> StorageFlush for &mut S
where
S: StorageFlush + ?Sized,
{
type Error = S::Error;
fn flush(&mut self) -> Result<(), Self::Error> {
(**self).flush()
}
}
use std::collections::{BTreeSet, HashSet, VecDeque};
use std::convert::Infallible;
use std::error::Error;
use std::fmt::Debug;
use std::fmt::{self};
use crate::blob::encodings::UnknownBlob;
use crate::blob::Blob;
use crate::blob::BlobEncoding;
use crate::blob::IntoBlob;
use crate::collection::{
CollectionData, CollectionRead, CollectionRecordSelector, CollectionStore, CoverageRead,
};
use crate::inline::encodings::hash::Handle;
use crate::inline::Inline;
use crate::inline::InlineEncoding;
use crate::inline::INLINE_LEN;
use crate::patch::IdentitySchema;
use crate::patch::PATCH;
use crate::prelude::inlineencodings::GenId;
use crate::trible::{TribleSet, V_END, V_START};
use crate::blob::encodings::simplearchive::SimpleArchive;
use crate::blob::encodings::utf8string::UTF8String;
use crate::inline::encodings::shortstring::ShortString;
use crate::prelude::*;
attributes! {
"4DD4DDD05CC31734B03ABB4E43188B1F" unsafe as pub content: Handle<SimpleArchive>;
"317044B612C690000D798CA660ECFD2A" unsafe as pub parent: Handle<SimpleArchive>;
"B59D147839100B6ED4B165DF76EDF3BB" unsafe as pub message: Handle<UTF8String>;
"12290C0BE0E9207E324F24DDE0D89300" unsafe as pub short_message: ShortString;
"272FBC56108F336C4D2E17289468C35F" unsafe as pub head: Handle<SimpleArchive>;
"8694CC73AF96A5E1C7635C677D1B928A" unsafe as pub branch: GenId;
}
pub type CommitHandle = Inline<Handle<SimpleArchive>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlobInfo {
pub handle: Inline<Handle<UnknownBlob>>,
pub length: u64,
}
pub trait BlobStoreList {
type Iter<'a>: Iterator<Item = Result<BlobInfo, Self::Err>>
where
Self: 'a;
type Err: Error + Debug + Send + Sync + 'static;
fn blobs<'a>(&'a self) -> Self::Iter<'a>;
fn contains_blob<S>(&self, handle: Inline<Handle<S>>) -> Result<bool, Self::Err>
where
S: BlobEncoding + 'static,
Handle<S>: InlineEncoding,
{
for info in self.blobs() {
if info?.handle.raw == handle.raw {
return Ok(true);
}
}
Ok(false)
}
fn blob_info<S>(&self, handle: Inline<Handle<S>>) -> Result<Option<BlobInfo>, Self::Err>
where
S: BlobEncoding + 'static,
Handle<S>: InlineEncoding,
{
for info in self.blobs() {
let info = info?;
if info.handle.raw == handle.raw {
return Ok(Some(info));
}
}
Ok(None)
}
fn blobs_diff<'a>(&'a self, _old: &Self) -> Self::Iter<'a> {
self.blobs()
}
}
#[derive(Debug, Clone)]
pub struct BlobMetadata {
pub timestamp: u64,
pub length: u64,
}
pub trait BlobStoreMeta {
type MetaError: std::error::Error + Send + Sync + 'static;
fn metadata<S>(
&self,
handle: Inline<Handle<S>>,
) -> Result<Option<BlobMetadata>, Self::MetaError>
where
S: BlobEncoding + 'static,
Handle<S>: InlineEncoding;
fn resident(
&self,
handles: &PATCH<32, IdentitySchema, ()>,
) -> Result<PATCH<32, IdentitySchema, ()>, Self::MetaError> {
let mut resident = PATCH::new();
for key in handles.iter_ordered() {
if self
.metadata(Inline::<Handle<UnknownBlob>>::new(*key))?
.is_some()
{
resident.insert(&crate::patch::Entry::new(key));
}
}
Ok(resident)
}
}
pub trait BlobStoreForget {
type ForgetError: std::error::Error + Send + Sync + 'static;
fn forget<S>(&mut self, handle: Inline<Handle<S>>) -> Result<(), Self::ForgetError>
where
S: BlobEncoding + 'static,
Handle<S>: InlineEncoding;
}
pub trait BlobStoreGet {
type GetError<E: std::error::Error + Send + Sync + 'static>: Error + Send + Sync + 'static;
fn get<T, S>(
&self,
handle: Inline<Handle<S>>,
) -> Result<T, Self::GetError<<T as TryFromBlob<S>>::Error>>
where
S: BlobEncoding + 'static,
T: TryFromBlob<S>,
Handle<S>: InlineEncoding;
}
pub trait BlobStorePut {
type PutError: Error + Debug + Send + Sync + 'static;
fn put<S, T>(&mut self, item: T) -> Result<Inline<Handle<S>>, Self::PutError>
where
S: BlobEncoding + 'static,
T: IntoBlob<S>,
Handle<S>: InlineEncoding;
}
impl<B> BlobStorePut for &mut B
where
B: BlobStorePut + ?Sized,
{
type PutError = B::PutError;
fn put<S, T>(&mut self, item: T) -> Result<Inline<Handle<S>>, Self::PutError>
where
S: BlobEncoding + 'static,
T: IntoBlob<S>,
Handle<S>: InlineEncoding,
{
(**self).put(item)
}
}
pub trait BlobStore: BlobStorePut + SnapshotSource<Snapshot: BlobStoreGet + BlobStoreList> {}
impl<B> BlobStore for B
where
B: BlobStorePut + SnapshotSource + ?Sized,
B::Snapshot: BlobStoreGet + BlobStoreList,
{
}
pub trait StoreRead:
StoreSnapshot
+ BlobStoreGet
+ BlobStoreList
+ BlobStoreMeta
+ CollectionRead
+ CoverageRead
+ CapabilityProofRead
+ WantRead
{
}
impl<R> StoreRead for R where
R: StoreSnapshot
+ BlobStoreGet
+ BlobStoreList
+ BlobStoreMeta
+ CollectionRead
+ CoverageRead
+ CapabilityProofRead
+ WantRead
{
}
pub trait Store:
BlobStore + CollectionStore + CapabilityProofStore + SnapshotSource<Snapshot: StoreRead>
{
}
impl<S> Store for S
where
S: BlobStore + CollectionStore + CapabilityProofStore + SnapshotSource,
S::Snapshot: StoreRead,
{
}
pub trait BlobStoreKeep {
fn keep<I>(&mut self, handles: I)
where
I: IntoIterator<Item = Inline<Handle<UnknownBlob>>>;
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct RetentionRoots {
direct: BTreeSet<[u8; INLINE_LEN]>,
recursive: BTreeSet<[u8; INLINE_LEN]>,
}
impl RetentionRoots {
pub fn new() -> Self {
Self::default()
}
pub fn retain_direct<S>(&mut self, handle: Inline<Handle<S>>)
where
S: BlobEncoding + 'static,
Handle<S>: InlineEncoding,
{
self.direct.insert(handle.raw);
}
pub fn retain_recursive<S>(&mut self, handle: Inline<Handle<S>>)
where
S: BlobEncoding + 'static,
Handle<S>: InlineEncoding,
{
self.recursive.insert(handle.raw);
}
pub fn union(&mut self, other: Self) {
self.direct.extend(other.direct);
self.recursive.extend(other.recursive);
}
pub fn direct(&self) -> impl ExactSizeIterator<Item = Inline<Handle<UnknownBlob>>> + '_ {
self.direct
.iter()
.copied()
.map(Inline::<Handle<UnknownBlob>>::new)
}
pub fn recursive(&self) -> impl ExactSizeIterator<Item = Inline<Handle<UnknownBlob>>> + '_ {
self.recursive
.iter()
.copied()
.map(Inline::<Handle<UnknownBlob>>::new)
}
pub fn expanded<R>(&self, reader: &R) -> BTreeSet<Inline<Handle<UnknownBlob>>>
where
R: BlobChildren,
{
let mut keep: BTreeSet<_> = self.direct().collect();
keep.extend(reachable(reader, self.recursive()));
keep
}
pub fn is_empty(&self) -> bool {
self.direct.is_empty() && self.recursive.is_empty()
}
}
pub trait BlobChildren: BlobStoreGet {
fn children(&self, handle: Inline<Handle<UnknownBlob>>) -> Vec<Inline<Handle<UnknownBlob>>> {
let Ok(blob) = self.get::<Blob<UnknownBlob>, UnknownBlob>(handle) else {
return Vec::new();
};
let bytes = blob.bytes.as_ref();
let mut result = Vec::new();
let mut offset = 0usize;
while offset + INLINE_LEN <= bytes.len() {
let mut raw = [0u8; INLINE_LEN];
raw.copy_from_slice(&bytes[offset..offset + INLINE_LEN]);
let candidate = Inline::<Handle<UnknownBlob>>::new(raw);
if self.get::<anybytes::Bytes, UnknownBlob>(candidate).is_ok() {
result.push(candidate);
}
offset += INLINE_LEN;
}
result
}
}
pub type PinSnapshot = PATCH<16, IdentitySchema, Inline<Handle<SimpleArchive>>>;
pub trait PinSnapshotSource {
type PinSnapshotError: Error + Debug + Send + Sync + 'static;
fn snapshot_pin_heads(&mut self) -> Result<PinSnapshot, Self::PinSnapshotError>;
}
#[cfg(test)]
mod pin_snapshot_source_tests {
use super::PinSnapshotSource;
use crate::repo::pile::Pile;
fn assert_source<T: PinSnapshotSource>() {}
#[test]
fn only_pile_exposes_legacy_pin_inspection() {
assert_source::<Pile>();
}
}
pub const WANT_REQUEST_BYTES_LEN: usize = 1 + 3 * INLINE_LEN;
pub const WANT_REQUEST_KIND_BLOB_V1: u8 = 1;
#[cfg(test)]
pub(crate) const WANT_REQUEST_KIND_DERIVE_V1: u8 = 3;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum WantRequest {
Blob {
handle: Inline<Handle<UnknownBlob>>,
},
}
impl WantRequest {
pub fn blob<S>(handle: Inline<Handle<S>>) -> Self
where
S: BlobEncoding + 'static,
Handle<S>: InlineEncoding,
{
Self::Blob {
handle: handle.transmute(),
}
}
pub const fn blob_handle(self) -> Option<Inline<Handle<UnknownBlob>>> {
match self {
Self::Blob { handle } => Some(handle),
}
}
pub fn blob_references(self) -> impl ExactSizeIterator<Item = Inline<Handle<UnknownBlob>>> {
let mut references = arrayvec::ArrayVec::<_, 3>::new();
match self {
Self::Blob { handle } => references.push(handle),
}
references.into_iter()
}
pub fn to_bytes(self) -> [u8; WANT_REQUEST_BYTES_LEN] {
let mut bytes = [0; WANT_REQUEST_BYTES_LEN];
match self {
Self::Blob { handle } => {
bytes[0] = WANT_REQUEST_KIND_BLOB_V1;
write_want_field(&mut bytes, 0, handle.raw);
}
}
bytes
}
pub fn from_bytes(bytes: [u8; WANT_REQUEST_BYTES_LEN]) -> Result<Self, WantRequestDecodeError> {
match bytes[0] {
WANT_REQUEST_KIND_BLOB_V1 => {
if bytes[1 + INLINE_LEN..].iter().any(|byte| *byte != 0) {
return Err(WantRequestDecodeError::NonZeroUnusedFields {
kind: WANT_REQUEST_KIND_BLOB_V1,
});
}
Ok(Self::Blob {
handle: Inline::new(read_want_field(&bytes, 0)),
})
}
unknown => Err(WantRequestDecodeError::UnknownKind(unknown)),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WantRequestDecodeError {
UnknownKind(u8),
NonZeroUnusedFields { kind: u8 },
}
impl fmt::Display for WantRequestDecodeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnknownKind(kind) => {
write!(formatter, "want request has unknown dense kind {kind}")
}
Self::NonZeroUnusedFields { kind } => write!(
formatter,
"want request kind {kind} has non-zero unused fields"
),
}
}
}
impl Error for WantRequestDecodeError {}
fn write_want_field(
bytes: &mut [u8; WANT_REQUEST_BYTES_LEN],
index: usize,
field: [u8; INLINE_LEN],
) {
let start = 1 + index * INLINE_LEN;
bytes[start..start + INLINE_LEN].copy_from_slice(&field);
}
fn read_want_field(bytes: &[u8; WANT_REQUEST_BYTES_LEN], index: usize) -> [u8; INLINE_LEN] {
let start = 1 + index * INLINE_LEN;
bytes[start..start + INLINE_LEN]
.try_into()
.expect("validated fixed-width want request field")
}
pub trait WantRead {
type WantsError: Error + Debug + Send + Sync + 'static;
type WantIter<'a>: Iterator<Item = Result<WantRequest, Self::WantsError>>
where
Self: 'a;
fn wants<'a>(&'a self) -> Result<Self::WantIter<'a>, Self::WantsError>;
}
pub trait WantStore {
type WantError: Error + Debug + Send + Sync + 'static;
fn want(&mut self, request: WantRequest) -> Result<(), Self::WantError>;
}
#[cfg(test)]
mod want_request_tests {
use super::*;
#[test]
fn typed_blob_request_roundtrips_with_zero_unused_fields() {
let typed = Inline::<Handle<SimpleArchive>>::new([0x41; INLINE_LEN]);
let request = WantRequest::blob(typed);
let bytes = request.to_bytes();
assert_eq!(bytes.len(), WANT_REQUEST_BYTES_LEN);
assert_eq!(bytes[0], WANT_REQUEST_KIND_BLOB_V1);
assert!(bytes[1 + INLINE_LEN..].iter().all(|byte| *byte == 0));
assert_eq!(WantRequest::from_bytes(bytes), Ok(request));
assert_eq!(
request,
WantRequest::Blob {
handle: typed.transmute()
}
);
}
#[test]
fn wants_enumerate_every_direct_blob_reference() {
let blob = WantRequest::blob(Inline::<Handle<UnknownBlob>>::new([1; INLINE_LEN]));
assert_eq!(
blob.blob_references()
.map(|handle| handle.raw)
.collect::<Vec<_>>(),
vec![[1; INLINE_LEN]],
);
}
#[test]
fn dense_decoder_rejects_noncanonical_shapes() {
let mut unknown = [0; WANT_REQUEST_BYTES_LEN];
unknown[0] = 99;
assert_eq!(
WantRequest::from_bytes(unknown),
Err(WantRequestDecodeError::UnknownKind(99))
);
let mut padded =
WantRequest::blob(Inline::<Handle<UnknownBlob>>::new([0x51; INLINE_LEN])).to_bytes();
padded[1 + INLINE_LEN] = 1;
assert_eq!(
WantRequest::from_bytes(padded),
Err(WantRequestDecodeError::NonZeroUnusedFields {
kind: WANT_REQUEST_KIND_BLOB_V1,
})
);
}
}
#[derive(Debug)]
pub enum TransferError<ListErr, LoadErr, StoreErr> {
List(ListErr),
Load(LoadErr),
Store(StoreErr),
}
impl<ListErr, LoadErr, StoreErr> fmt::Display for TransferError<ListErr, LoadErr, StoreErr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "failed to transfer blob")
}
}
impl<ListErr, LoadErr, StoreErr> Error for TransferError<ListErr, LoadErr, StoreErr>
where
ListErr: Debug + Error + 'static,
LoadErr: Debug + Error + 'static,
StoreErr: Debug + Error + 'static,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::List(e) => Some(e),
Self::Load(e) => Some(e),
Self::Store(e) => Some(e),
}
}
}
pub fn transfer<'a, BS, BT, Handles>(
source: &'a BS,
target: &'a mut BT,
handles: Handles,
) -> impl Iterator<
Item = Result<
(Inline<Handle<UnknownBlob>>, Inline<Handle<UnknownBlob>>),
TransferError<
Infallible,
<BS as BlobStoreGet>::GetError<Infallible>,
<BT as BlobStorePut>::PutError,
>,
>,
> + 'a
where
BS: BlobStoreGet + 'a,
BT: BlobStorePut + 'a,
Handles: IntoIterator<Item = Inline<Handle<UnknownBlob>>> + 'a,
Handles::IntoIter: 'a,
{
handles.into_iter().map(move |source_handle| {
let blob: Blob<UnknownBlob> = source.get(source_handle).map_err(TransferError::Load)?;
Ok((
source_handle,
(target.put(blob).map_err(TransferError::Store)?),
))
})
}
pub struct ReachableHandles<'a, BS>
where
BS: BlobChildren,
{
source: &'a BS,
queue: VecDeque<Inline<Handle<UnknownBlob>>>,
visited: HashSet<[u8; INLINE_LEN]>,
}
impl<'a, BS> ReachableHandles<'a, BS>
where
BS: BlobChildren,
{
fn new(source: &'a BS, roots: impl IntoIterator<Item = Inline<Handle<UnknownBlob>>>) -> Self {
let mut queue = VecDeque::new();
for handle in roots {
queue.push_back(handle);
}
Self {
source,
queue,
visited: HashSet::new(),
}
}
}
impl<'a, BS> Iterator for ReachableHandles<'a, BS>
where
BS: BlobChildren,
{
type Item = Inline<Handle<UnknownBlob>>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(handle) = self.queue.pop_front() {
let raw = handle.raw;
if !self.visited.insert(raw) {
continue;
}
for child in self.source.children(handle) {
if !self.visited.contains(&child.raw) {
self.queue.push_back(child);
}
}
return Some(handle);
}
None
}
}
pub fn reachable<'a, BS>(
source: &'a BS,
roots: impl IntoIterator<Item = Inline<Handle<UnknownBlob>>>,
) -> ReachableHandles<'a, BS>
where
BS: BlobChildren,
{
ReachableHandles::new(source, roots)
}
pub fn potential_handles<'a>(
set: &'a TribleSet,
) -> impl Iterator<Item = Inline<Handle<UnknownBlob>>> + 'a {
set.vae.iter().map(|raw| {
let mut value = [0u8; INLINE_LEN];
value.copy_from_slice(&raw[V_START..=V_END]);
Inline::<Handle<UnknownBlob>>::new(value)
})
}