use std::{ cell::RefCell, convert::{TryFrom, TryInto}, ops::Deref, rc::Rc, sync::{atomic::Ordering, Arc},}; use git_features::threading::OwnShared;use git_hash::oid; use crate::store::{handle, types, RefreshMode}; pub(crate) mod multi_index { // TODO: remove this declaration and replace it with the actual type where it's used pub type File = git_pack::multi_index::File;} pub enum SingleOrMultiIndex { Single { index: Arc<git_pack::index::File>, data: Option<Arc<git_pack::data::File>>, }, Multi { index: Arc<multi_index::File>, data: Vec<Option<Arc<git_pack::data::File>>>, },} /// A utility to allow looking up pack offsets for a particular packpub enum IntraPackLookup<'a> { Single(&'a git_pack::index::File), /// the internal pack-id inside of a multi-index for which the lookup is supposed to be. /// Used to prevent ref-delta OIDs to, for some reason, point to a different pack. Multi { index: &'a git_pack::multi_index::File, required_pack_index: git_pack::multi_index::PackIndex, },} impl<'a> IntraPackLookup<'a> { pub(crate) fn pack_offset_by_id(&self, id: &oid) -> Option<git_pack::data::Offset> { match self { IntraPackLookup::Single(index) => index .lookup(id) .map(|entry_index| index.pack_offset_at_index(entry_index)), IntraPackLookup::Multi { index, required_pack_index, } => index.lookup(id).and_then(|entry_index| { let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(entry_index); (pack_index == *required_pack_index).then(|| pack_offset) }), } }} pub struct IndexLookup { pub(crate) file: SingleOrMultiIndex, /// The index we were found at in the slot map pub(crate) id: types::IndexId,} pub struct IndexForObjectInPack { /// The internal identifier of the pack itself, which either is referred to by an index or a multi-pack index. pub(crate) pack_id: types::PackId, /// The offset at which the object's entry can be found pub(crate) pack_offset: u64,} pub(crate) mod index_lookup { use std::sync::Arc; use git_hash::oid; use crate::store::{handle, handle::IntraPackLookup, types}; pub(crate) struct Outcome<'a> { pub object_index: handle::IndexForObjectInPack, pub index_file: IntraPackLookup<'a>, pub pack: &'a mut Option<Arc<git_pack::data::File>>, } impl handle::IndexLookup { /// Return an iterator over the entries of the given pack. The `pack_id` is only required to pub(crate) fn iter( &self, pack_id: types::PackId, ) -> Option<Box<dyn Iterator<Item = git_pack::index::Entry> + '_>> { (self.id == pack_id.index).then(|| match &self.file { handle::SingleOrMultiIndex::Single { index, .. } => index.iter(), handle::SingleOrMultiIndex::Multi { index, .. } => { let pack_index = pack_id.multipack_index.expect( "BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable", ); Box::new(index.iter().filter_map(move |e| { (e.pack_index == pack_index as u32).then(|| git_pack::index::Entry { oid: e.oid, pack_offset: e.pack_offset, crc32: None, }) })) } }) } pub(crate) fn pack(&mut self, pack_id: types::PackId) -> Option<&'_ mut Option<Arc<git_pack::data::File>>> { (self.id == pack_id.index).then(move || match &mut self.file { handle::SingleOrMultiIndex::Single { data, .. } => data, handle::SingleOrMultiIndex::Multi { data, .. } => { let pack_index = pack_id.multipack_index.expect( "BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable", ); &mut data[pack_index as usize] } }) } /// Return true if the given object id exists in this index pub(crate) fn contains(&self, object_id: &oid) -> bool { match &self.file { handle::SingleOrMultiIndex::Single { index, .. } => index.lookup(object_id).is_some(), handle::SingleOrMultiIndex::Multi { index, .. } => index.lookup(object_id).is_some(), } } /// Return true if the given object id exists in this index pub(crate) fn oid_at_index(&self, entry_index: u32) -> &git_hash::oid { match &self.file { handle::SingleOrMultiIndex::Single { index, .. } => index.oid_at_index(entry_index), handle::SingleOrMultiIndex::Multi { index, .. } => index.oid_at_index(entry_index), } } /// Return the amount of objects contained in the index, essentially the number of object ids. pub(crate) fn num_objects(&self) -> u32 { match &self.file { handle::SingleOrMultiIndex::Single { index, .. } => index.num_objects(), handle::SingleOrMultiIndex::Multi { index, .. } => index.num_objects(), } } /// Call `lookup_prefix(…)` on either index or multi-index, and transform matches into an object id. pub(crate) fn lookup_prefix(&self, prefix: git_hash::Prefix) -> Option<crate::find::PrefixLookupResult> { let res = match &self.file { handle::SingleOrMultiIndex::Single { index, .. } => index.lookup_prefix(prefix), handle::SingleOrMultiIndex::Multi { index, .. } => index.lookup_prefix(prefix), }?; Some(res.map(|entry_index| self.oid_at_index(entry_index).to_owned())) } /// See if the oid is contained in this index, and return its full id for lookup possibly alongside its data file if already /// loaded. /// Also return the index itself as it's needed to resolve intra-pack ref-delta objects. They are a possibility even though /// they won't be used in practice as it's more efficient to store their offsets. /// If it is not loaded, ask it to be loaded and put it into the returned mutable option for safe-keeping. pub(crate) fn lookup(&mut self, object_id: &oid) -> Option<Outcome<'_>> { let id = self.id; match &mut self.file { handle::SingleOrMultiIndex::Single { index, data } => index.lookup(object_id).map(move |idx| Outcome { object_index: handle::IndexForObjectInPack { pack_id: types::PackId { index: id, multipack_index: None, }, pack_offset: index.pack_offset_at_index(idx), }, index_file: IntraPackLookup::Single(&**index), pack: data, }), handle::SingleOrMultiIndex::Multi { index, data } => index.lookup(object_id).map(move |idx| { let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(idx); Outcome { object_index: handle::IndexForObjectInPack { pack_id: types::PackId { index: id, multipack_index: Some(pack_index), }, pack_offset, }, index_file: IntraPackLookup::Multi { index: &**index, required_pack_index: pack_index, }, pack: &mut data[pack_index as usize], } }), } } }} pub(crate) enum Mode { DeletedPacksAreInaccessible, /// This mode signals that we should not unload packs even after they went missing. KeepDeletedPacksAvailable,} /// Handle registrationimpl super::Store { pub(crate) fn register_handle(&self) -> Mode { self.num_handles_unstable.fetch_add(1, Ordering::Relaxed); Mode::DeletedPacksAreInaccessible } pub(crate) fn remove_handle(&self, mode: Mode) { match mode { Mode::KeepDeletedPacksAvailable => { let _lock = self.write.lock(); self.num_handles_stable.fetch_sub(1, Ordering::SeqCst) } Mode::DeletedPacksAreInaccessible => self.num_handles_unstable.fetch_sub(1, Ordering::Relaxed), }; } pub(crate) fn upgrade_handle(&self, mode: Mode) -> Mode { if let Mode::DeletedPacksAreInaccessible = mode { let _lock = self.write.lock(); self.num_handles_stable.fetch_add(1, Ordering::SeqCst); self.num_handles_unstable.fetch_sub(1, Ordering::SeqCst); } Mode::KeepDeletedPacksAvailable }} /// Handle creationimpl super::Store { /// The amount of times a ref-delta base can be followed when multi-indices are involved. pub const INITIAL_MAX_RECURSION_DEPTH: usize = 32; /// Create a new cache filled with a handle to this store, if this store is supporting shared ownership. /// /// Note that the actual type of `OwnShared` depends on the `parallel` feature toggle of the `git-features` crate. pub fn to_cache(self: &OwnShared<Self>) -> crate::Cache<super::Handle<OwnShared<super::Store>>> { self.to_handle().into() } /// Create a new cache filled with a handle to this store if this store is held in an `Arc`. pub fn to_cache_arc(self: &Arc<Self>) -> crate::Cache<super::Handle<Arc<super::Store>>> { self.to_handle_arc().into() } /// Create a new database handle to this store if this store is supporting shared ownership. /// /// See also, [`to_cache()`][super::Store::to_cache()] which is probably more useful. pub fn to_handle(self: &OwnShared<Self>) -> super::Handle<OwnShared<super::Store>> { let token = self.register_handle(); super::Handle { store: self.clone(), refresh: RefreshMode::default(), ignore_replacements: false, token: Some(token), snapshot: RefCell::new(self.collect_snapshot()), max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH, packed_object_count: Default::default(), } } /// Create a new database handle to this store if this store is held in an `Arc`. /// /// This method is useful in applications that know they will use threads. pub fn to_handle_arc(self: &Arc<Self>) -> super::Handle<Arc<super::Store>> { let token = self.register_handle(); super::Handle { store: self.clone(), refresh: Default::default(), ignore_replacements: false, token: Some(token), snapshot: RefCell::new(self.collect_snapshot()), max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH, packed_object_count: Default::default(), } } /// Transform the only instance into an `Arc<Self>` or panic if this is not the only Rc handle /// to the contained store. /// /// This is meant to be used when the `git_features::threading::OwnShared` refers to an `Rc` as it was compiled without the /// `parallel` feature toggle. pub fn into_shared_arc(self: OwnShared<Self>) -> Arc<Self> { match OwnShared::try_unwrap(self) { Ok(this) => Arc::new(this), Err(_) => panic!("BUG: Must be called when there is only one owner for this RC"), } }} impl<S> super::Handle<S>where S: Deref<Target = super::Store> + Clone,{ /// Call once if pack ids are stored and later used for lookup, meaning they should always remain mapped and not be unloaded /// even if they disappear from disk. /// This must be called if there is a chance that git maintenance is happening while a pack is created. pub fn prevent_pack_unload(&mut self) { self.token = self.token.take().map(|token| self.store.upgrade_handle(token)); } /// Return a shared reference to the contained store. pub fn store_ref(&self) -> &S::Target { &*self.store } /// Return an owned store with shared ownership. pub fn store(&self) -> S { self.store.clone() } /// Set the handle to never cause ODB refreshes if an object could not be found. /// /// The latter is the default, as typically all objects referenced in a git repository are contained in the local clone. /// More recently, however, this doesn't always have to be the case due to sparse checkouts and other ways to only have a /// limited amount of objects available locally. pub fn refresh_never(&mut self) { self.refresh = RefreshMode::Never; } /// Return the current refresh mode. pub fn refresh_mode(&mut self) -> RefreshMode { self.refresh }} impl<S> Drop for super::Handle<S>where S: Deref<Target = super::Store> + Clone,{ fn drop(&mut self) { if let Some(token) = self.token.take() { self.store.remove_handle(token) } }} impl TryFrom<&super::Store> for super::Store { type Error = std::io::Error; fn try_from(s: &super::Store) -> Result<Self, Self::Error> { super::Store::at_opts( s.path(), s.replacements(), crate::store::init::Options { slots: crate::store::init::Slots::Given(s.files.len().try_into().expect("BUG: too many slots")), object_hash: Default::default(), use_multi_pack_index: false, }, ) }} impl super::Handle<Rc<super::Store>> { /// Convert a ref counted store into one that is ref-counted and thread-safe, by creating a new Store. pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> { let store = Arc::new(super::Store::try_from(self.store_ref())?); let mut cache = store.to_handle_arc(); cache.refresh = self.refresh; cache.max_recursion_depth = self.max_recursion_depth; Ok(cache) }} impl super::Handle<Arc<super::Store>> { /// Convert a ref counted store into one that is ref-counted and thread-safe, by creating a new Store pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> { Ok(self) }} impl<S> Clone for super::Handle<S>where S: Deref<Target = super::Store> + Clone,{ fn clone(&self) -> Self { super::Handle { store: self.store.clone(), refresh: self.refresh, ignore_replacements: self.ignore_replacements, token: { let token = self.store.register_handle(); match self.token.as_ref().expect("token is always set here ") { handle::Mode::DeletedPacksAreInaccessible => token, handle::Mode::KeepDeletedPacksAvailable => self.store.upgrade_handle(token), } .into() }, snapshot: RefCell::new(self.store.collect_snapshot()), max_recursion_depth: self.max_recursion_depth, packed_object_count: Default::default(), } }}
use std::{
cell::RefCell,
convert::{TryFrom, TryInto},
ops::Deref,
rc::Rc,
sync::{atomic::Ordering, Arc},
};
use git_features::threading::OwnShared;
use git_hash::oid;
use crate::store::{handle, types, RefreshMode};
pub(crate) mod multi_index {
// TODO: remove this declaration and replace it with the actual type where it's used
pub type File = git_pack::multi_index::File;
}
pub enum SingleOrMultiIndex {
Single {
index: Arc<git_pack::index::File>,
data: Option<Arc<git_pack::data::File>>,
},
Multi {
index: Arc<multi_index::File>,
data: Vec<Option<Arc<git_pack::data::File>>>,
},
}
/// A utility to allow looking up pack offsets for a particular pack
pub enum IntraPackLookup<'a> {
Single(&'a git_pack::index::File),
/// the internal pack-id inside of a multi-index for which the lookup is supposed to be.
/// Used to prevent ref-delta OIDs to, for some reason, point to a different pack.
Multi {
index: &'a git_pack::multi_index::File,
required_pack_index: git_pack::multi_index::PackIndex,
},
}
impl<'a> IntraPackLookup<'a> {
pub(crate) fn pack_offset_by_id(&self, id: &oid) -> Option<git_pack::data::Offset> {
match self {
IntraPackLookup::Single(index) => index
.lookup(id)
.map(|entry_index| index.pack_offset_at_index(entry_index)),
IntraPackLookup::Multi {
index,
required_pack_index,
} => index.lookup(id).and_then(|entry_index| {
let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(entry_index);
(pack_index == *required_pack_index).then(|| pack_offset)
}),
}
}
}
pub struct IndexLookup {
pub(crate) file: SingleOrMultiIndex,
/// The index we were found at in the slot map
pub(crate) id: types::IndexId,
}
pub struct IndexForObjectInPack {
/// The internal identifier of the pack itself, which either is referred to by an index or a multi-pack index.
pub(crate) pack_id: types::PackId,
/// The offset at which the object's entry can be found
pub(crate) pack_offset: u64,
}
pub(crate) mod index_lookup {
use std::sync::Arc;
use git_hash::oid;
use crate::store::{handle, handle::IntraPackLookup, types};
pub(crate) struct Outcome<'a> {
pub object_index: handle::IndexForObjectInPack,
pub index_file: IntraPackLookup<'a>,
pub pack: &'a mut Option<Arc<git_pack::data::File>>,
}
impl handle::IndexLookup {
/// Return an iterator over the entries of the given pack. The `pack_id` is only required to
pub(crate) fn iter(
&self,
pack_id: types::PackId,
) -> Option<Box<dyn Iterator<Item = git_pack::index::Entry> + '_>> {
(self.id == pack_id.index).then(|| match &self.file {
handle::SingleOrMultiIndex::Single { index, .. } => index.iter(),
handle::SingleOrMultiIndex::Multi { index, .. } => {
let pack_index = pack_id.multipack_index.expect(
"BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable",
);
Box::new(index.iter().filter_map(move |e| {
(e.pack_index == pack_index as u32).then(|| git_pack::index::Entry {
oid: e.oid,
pack_offset: e.pack_offset,
crc32: None,
})
}))
}
})
}
pub(crate) fn pack(&mut self, pack_id: types::PackId) -> Option<&'_ mut Option<Arc<git_pack::data::File>>> {
(self.id == pack_id.index).then(move || match &mut self.file {
handle::SingleOrMultiIndex::Single { data, .. } => data,
handle::SingleOrMultiIndex::Multi { data, .. } => {
let pack_index = pack_id.multipack_index.expect(
"BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable",
);
&mut data[pack_index as usize]
}
})
}
/// Return true if the given object id exists in this index
pub(crate) fn contains(&self, object_id: &oid) -> bool {
match &self.file {
handle::SingleOrMultiIndex::Single { index, .. } => index.lookup(object_id).is_some(),
handle::SingleOrMultiIndex::Multi { index, .. } => index.lookup(object_id).is_some(),
}
}
/// Return true if the given object id exists in this index
pub(crate) fn oid_at_index(&self, entry_index: u32) -> &git_hash::oid {
match &self.file {
handle::SingleOrMultiIndex::Single { index, .. } => index.oid_at_index(entry_index),
handle::SingleOrMultiIndex::Multi { index, .. } => index.oid_at_index(entry_index),
}
}
/// Return the amount of objects contained in the index, essentially the number of object ids.
pub(crate) fn num_objects(&self) -> u32 {
match &self.file {
handle::SingleOrMultiIndex::Single { index, .. } => index.num_objects(),
handle::SingleOrMultiIndex::Multi { index, .. } => index.num_objects(),
}
}
/// Call `lookup_prefix(…)` on either index or multi-index, and transform matches into an object id.
pub(crate) fn lookup_prefix(&self, prefix: git_hash::Prefix) -> Option<crate::find::PrefixLookupResult> {
let res = match &self.file {
handle::SingleOrMultiIndex::Single { index, .. } => index.lookup_prefix(prefix),
handle::SingleOrMultiIndex::Multi { index, .. } => index.lookup_prefix(prefix),
}?;
Some(res.map(|entry_index| self.oid_at_index(entry_index).to_owned()))
}
/// See if the oid is contained in this index, and return its full id for lookup possibly alongside its data file if already
/// loaded.
/// Also return the index itself as it's needed to resolve intra-pack ref-delta objects. They are a possibility even though
/// they won't be used in practice as it's more efficient to store their offsets.
/// If it is not loaded, ask it to be loaded and put it into the returned mutable option for safe-keeping.
pub(crate) fn lookup(&mut self, object_id: &oid) -> Option<Outcome<'_>> {
let id = self.id;
match &mut self.file {
handle::SingleOrMultiIndex::Single { index, data } => index.lookup(object_id).map(move |idx| Outcome {
object_index: handle::IndexForObjectInPack {
pack_id: types::PackId {
index: id,
multipack_index: None,
},
pack_offset: index.pack_offset_at_index(idx),
},
index_file: IntraPackLookup::Single(&**index),
pack: data,
}),
handle::SingleOrMultiIndex::Multi { index, data } => index.lookup(object_id).map(move |idx| {
let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(idx);
Outcome {
object_index: handle::IndexForObjectInPack {
pack_id: types::PackId {
index: id,
multipack_index: Some(pack_index),
},
pack_offset,
},
index_file: IntraPackLookup::Multi {
index: &**index,
required_pack_index: pack_index,
},
pack: &mut data[pack_index as usize],
}
}),
}
}
}
}
pub(crate) enum Mode {
DeletedPacksAreInaccessible,
/// This mode signals that we should not unload packs even after they went missing.
KeepDeletedPacksAvailable,
}
/// Handle registration
impl super::Store {
pub(crate) fn register_handle(&self) -> Mode {
self.num_handles_unstable.fetch_add(1, Ordering::Relaxed);
Mode::DeletedPacksAreInaccessible
}
pub(crate) fn remove_handle(&self, mode: Mode) {
match mode {
Mode::KeepDeletedPacksAvailable => {
let _lock = self.write.lock();
self.num_handles_stable.fetch_sub(1, Ordering::SeqCst)
}
Mode::DeletedPacksAreInaccessible => self.num_handles_unstable.fetch_sub(1, Ordering::Relaxed),
};
}
pub(crate) fn upgrade_handle(&self, mode: Mode) -> Mode {
if let Mode::DeletedPacksAreInaccessible = mode {
let _lock = self.write.lock();
self.num_handles_stable.fetch_add(1, Ordering::SeqCst);
self.num_handles_unstable.fetch_sub(1, Ordering::SeqCst);
}
Mode::KeepDeletedPacksAvailable
}
}
/// Handle creation
impl super::Store {
/// The amount of times a ref-delta base can be followed when multi-indices are involved.
pub const INITIAL_MAX_RECURSION_DEPTH: usize = 32;
/// Create a new cache filled with a handle to this store, if this store is supporting shared ownership.
///
/// Note that the actual type of `OwnShared` depends on the `parallel` feature toggle of the `git-features` crate.
pub fn to_cache(self: &OwnShared<Self>) -> crate::Cache<super::Handle<OwnShared<super::Store>>> {
self.to_handle().into()
}
/// Create a new cache filled with a handle to this store if this store is held in an `Arc`.
pub fn to_cache_arc(self: &Arc<Self>) -> crate::Cache<super::Handle<Arc<super::Store>>> {
self.to_handle_arc().into()
}
/// Create a new database handle to this store if this store is supporting shared ownership.
///
/// See also, [`to_cache()`][super::Store::to_cache()] which is probably more useful.
pub fn to_handle(self: &OwnShared<Self>) -> super::Handle<OwnShared<super::Store>> {
let token = self.register_handle();
super::Handle {
store: self.clone(),
refresh: RefreshMode::default(),
ignore_replacements: false,
token: Some(token),
snapshot: RefCell::new(self.collect_snapshot()),
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
packed_object_count: Default::default(),
}
}
/// Create a new database handle to this store if this store is held in an `Arc`.
///
/// This method is useful in applications that know they will use threads.
pub fn to_handle_arc(self: &Arc<Self>) -> super::Handle<Arc<super::Store>> {
let token = self.register_handle();
super::Handle {
store: self.clone(),
refresh: Default::default(),
ignore_replacements: false,
token: Some(token),
snapshot: RefCell::new(self.collect_snapshot()),
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
packed_object_count: Default::default(),
}
}
/// Transform the only instance into an `Arc<Self>` or panic if this is not the only Rc handle
/// to the contained store.
///
/// This is meant to be used when the `git_features::threading::OwnShared` refers to an `Rc` as it was compiled without the
/// `parallel` feature toggle.
pub fn into_shared_arc(self: OwnShared<Self>) -> Arc<Self> {
match OwnShared::try_unwrap(self) {
Ok(this) => Arc::new(this),
Err(_) => panic!("BUG: Must be called when there is only one owner for this RC"),
}
}
}
impl<S> super::Handle<S>
where
S: Deref<Target = super::Store> + Clone,
{
/// Call once if pack ids are stored and later used for lookup, meaning they should always remain mapped and not be unloaded
/// even if they disappear from disk.
/// This must be called if there is a chance that git maintenance is happening while a pack is created.
pub fn prevent_pack_unload(&mut self) {
self.token = self.token.take().map(|token| self.store.upgrade_handle(token));
}
/// Return a shared reference to the contained store.
pub fn store_ref(&self) -> &S::Target {
&*self.store
}
/// Return an owned store with shared ownership.
pub fn store(&self) -> S {
self.store.clone()
}
/// Set the handle to never cause ODB refreshes if an object could not be found.
///
/// The latter is the default, as typically all objects referenced in a git repository are contained in the local clone.
/// More recently, however, this doesn't always have to be the case due to sparse checkouts and other ways to only have a
/// limited amount of objects available locally.
pub fn refresh_never(&mut self) {
self.refresh = RefreshMode::Never;
}
/// Return the current refresh mode.
pub fn refresh_mode(&mut self) -> RefreshMode {
self.refresh
}
}
impl<S> Drop for super::Handle<S>
where
S: Deref<Target = super::Store> + Clone,
{
fn drop(&mut self) {
if let Some(token) = self.token.take() {
self.store.remove_handle(token)
}
}
}
impl TryFrom<&super::Store> for super::Store {
type Error = std::io::Error;
fn try_from(s: &super::Store) -> Result<Self, Self::Error> {
super::Store::at_opts(
s.path(),
s.replacements(),
crate::store::init::Options {
slots: crate::store::init::Slots::Given(s.files.len().try_into().expect("BUG: too many slots")),
object_hash: Default::default(),
use_multi_pack_index: false,
},
)
}
}
impl super::Handle<Rc<super::Store>> {
/// Convert a ref counted store into one that is ref-counted and thread-safe, by creating a new Store.
pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> {
let store = Arc::new(super::Store::try_from(self.store_ref())?);
let mut cache = store.to_handle_arc();
cache.refresh = self.refresh;
cache.max_recursion_depth = self.max_recursion_depth;
Ok(cache)
}
}
impl super::Handle<Arc<super::Store>> {
/// Convert a ref counted store into one that is ref-counted and thread-safe, by creating a new Store
pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> {
Ok(self)
}
}
impl<S> Clone for super::Handle<S>
where
S: Deref<Target = super::Store> + Clone,
{
fn clone(&self) -> Self {
super::Handle {
store: self.store.clone(),
refresh: self.refresh,
ignore_replacements: self.ignore_replacements,
token: {
let token = self.store.register_handle();
match self.token.as_ref().expect("token is always set here ") {
handle::Mode::DeletedPacksAreInaccessible => token,
handle::Mode::KeepDeletedPacksAvailable => self.store.upgrade_handle(token),
}
.into()
},
snapshot: RefCell::new(self.store.collect_snapshot()),
max_recursion_depth: self.max_recursion_depth,
packed_object_count: Default::default(),
}
}
}