use std::{
cell::RefCell,
convert::{TryFrom, TryInto},
ops::Deref,
rc::Rc,
sync::{atomic::Ordering, Arc},
};
use gix_features::threading::OwnShared;
use gix_hash::oid;
use crate::store::{handle, types, RefreshMode};
pub(crate) enum SingleOrMultiIndex {
Single {
index: Arc<gix_pack::index::File>,
data: Option<Arc<gix_pack::data::File>>,
},
Multi {
index: Arc<gix_pack::multi_index::File>,
data: Vec<Option<Arc<gix_pack::data::File>>>,
},
}
pub(crate) enum IntraPackLookup<'a> {
Single(&'a gix_pack::index::File),
Multi {
index: &'a gix_pack::multi_index::File,
required_pack_index: gix_pack::multi_index::PackIndex,
},
}
impl<'a> IntraPackLookup<'a> {
pub(crate) fn pack_offset_by_id(&self, id: &oid) -> Option<gix_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_some(pack_offset)
}),
}
}
}
pub struct IndexLookup {
pub(crate) file: SingleOrMultiIndex,
pub(crate) id: types::IndexId,
}
pub struct IndexForObjectInPack {
pub(crate) pack_id: types::PackId,
pub(crate) pack_offset: u64,
}
pub(crate) mod index_lookup {
use std::{collections::HashSet, sync::Arc};
use gix_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<gix_pack::data::File>>,
}
impl handle::IndexLookup {
pub(crate) fn iter(
&self,
pack_id: types::PackId,
) -> Option<Box<dyn Iterator<Item = gix_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).then_some(gix_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<gix_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]
}
})
}
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(),
}
}
pub(crate) fn oid_at_index(&self, entry_index: u32) -> &gix_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),
}
}
pub(crate) fn num_objects(&self) -> u32 {
match &self.file {
handle::SingleOrMultiIndex::Single { index, .. } => index.num_objects(),
handle::SingleOrMultiIndex::Multi { index, .. } => index.num_objects(),
}
}
pub(crate) fn lookup_prefix(
&self,
prefix: gix_hash::Prefix,
candidates: Option<&mut HashSet<gix_hash::ObjectId>>,
) -> Option<crate::store::prefix::lookup::Outcome> {
let mut candidate_entries = candidates.as_ref().map(|_| 0..0);
let res = match &self.file {
handle::SingleOrMultiIndex::Single { index, .. } => {
index.lookup_prefix(prefix, candidate_entries.as_mut())
}
handle::SingleOrMultiIndex::Multi { index, .. } => {
index.lookup_prefix(prefix, candidate_entries.as_mut())
}
}?;
if let Some((candidates, entries)) = candidates.zip(candidate_entries) {
candidates.extend(entries.map(|entry| self.oid_at_index(entry).to_owned()));
}
Some(res.map(|entry_index| self.oid_at_index(entry_index).to_owned()))
}
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,
required_pack_index: pack_index,
},
pack: &mut data[pack_index as usize],
}
}),
}
}
}
}
pub(crate) enum Mode {
DeletedPacksAreInaccessible,
KeepDeletedPacksAvailable,
}
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
}
}
impl super::Store {
pub const INITIAL_MAX_RECURSION_DEPTH: usize = 32;
pub fn to_cache(self: &OwnShared<Self>) -> crate::Cache<super::Handle<OwnShared<super::Store>>> {
self.to_handle().into()
}
pub fn to_cache_arc(self: &Arc<Self>) -> crate::Cache<super::Handle<Arc<super::Store>>> {
self.to_handle_arc().into()
}
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),
inflate: RefCell::new(Default::default()),
snapshot: RefCell::new(self.collect_snapshot()),
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
packed_object_count: Default::default(),
}
}
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),
inflate: RefCell::new(Default::default()),
snapshot: RefCell::new(self.collect_snapshot()),
max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
packed_object_count: Default::default(),
}
}
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,
{
pub fn prevent_pack_unload(&mut self) {
self.token = self.token.take().map(|token| self.store.upgrade_handle(token));
}
pub fn store_ref(&self) -> &S::Target {
&self.store
}
pub fn store(&self) -> S {
self.store.clone()
}
pub fn refresh_never(&mut self) {
self.refresh = RefreshMode::Never;
}
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().into(),
&mut 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,
current_dir: s.current_dir.clone().into(),
},
)
}
}
impl super::Handle<Rc<super::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>> {
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()
},
inflate: RefCell::new(Default::default()),
snapshot: RefCell::new(self.store.collect_snapshot()),
max_recursion_depth: self.max_recursion_depth,
packed_object_count: Default::default(),
}
}
}