diff options
Diffstat (limited to 'vendor/gix-odb/src/cache.rs')
-rw-r--r-- | vendor/gix-odb/src/cache.rs | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/vendor/gix-odb/src/cache.rs b/vendor/gix-odb/src/cache.rs new file mode 100644 index 000000000..8e108646f --- /dev/null +++ b/vendor/gix-odb/src/cache.rs @@ -0,0 +1,234 @@ +use std::{ + cell::RefCell, + ops::{Deref, DerefMut}, + rc::Rc, + sync::Arc, +}; + +use crate::Cache; + +/// A type to store pack caches in boxes. +pub type PackCache = dyn gix_pack::cache::DecodeEntry + Send + 'static; +/// A constructor for boxed pack caches. +pub type NewPackCacheFn = dyn Fn() -> Box<PackCache> + Send + Sync + 'static; + +/// A type to store object caches in boxes. +pub type ObjectCache = dyn gix_pack::cache::Object + Send + 'static; +/// A constructor for boxed object caches. +pub type NewObjectCacheFn = dyn Fn() -> Box<ObjectCache> + Send + Sync + 'static; + +impl Cache<crate::store::Handle<Rc<crate::Store>>> { + /// Convert this cache's handle into one that keeps its store in an arc. This creates an entirely new store, + /// so should be done early to avoid unnecessary work (and mappings). + pub fn into_arc(self) -> std::io::Result<Cache<crate::store::Handle<Arc<crate::Store>>>> { + let inner = self.inner.into_arc()?; + Ok(Cache { + inner, + new_pack_cache: self.new_pack_cache, + new_object_cache: self.new_object_cache, + pack_cache: self.pack_cache, + object_cache: self.object_cache, + }) + } +} +impl Cache<crate::store::Handle<Arc<crate::Store>>> { + /// No op, as we are containing an arc handle already. + pub fn into_arc(self) -> std::io::Result<Cache<crate::store::Handle<Arc<crate::Store>>>> { + Ok(self) + } +} + +impl<S> Cache<S> { + /// Dissolve this instance, discard all caches, and return the inner implementation. + pub fn into_inner(self) -> S { + self.inner + } + /// Use this methods directly after creating a new instance to add a constructor for pack caches. + /// + /// These are used to speed up decoding objects which are located in packs, reducing long delta chains by storing + /// their intermediate results. + pub fn with_pack_cache(mut self, create: impl Fn() -> Box<PackCache> + Send + Sync + 'static) -> Self { + self.pack_cache = Some(RefCell::new(create())); + self.new_pack_cache = Some(Arc::new(create)); + self + } + /// Use this methods directly after creating a new instance to add a constructor for object caches. + /// + /// Only use this kind of cache if the same objects are repeatedly accessed for great speedups, usually during diffing of + /// trees. + pub fn with_object_cache(mut self, create: impl Fn() -> Box<ObjectCache> + Send + Sync + 'static) -> Self { + self.object_cache = Some(RefCell::new(create())); + self.new_object_cache = Some(Arc::new(create)); + self + } + /// Set the pack cache constructor on this instance. + pub fn set_pack_cache(&mut self, create: impl Fn() -> Box<PackCache> + Send + Sync + 'static) { + self.pack_cache = Some(RefCell::new(create())); + self.new_pack_cache = Some(Arc::new(create)); + } + /// Set the object cache constructor on this instance. + pub fn set_object_cache(&mut self, create: impl Fn() -> Box<ObjectCache> + Send + Sync + 'static) { + self.object_cache = Some(RefCell::new(create())); + self.new_object_cache = Some(Arc::new(create)); + } + /// Return true if an object cache is present. + pub fn has_object_cache(&self) -> bool { + self.object_cache.is_some() + } + /// Return true if a pack cache is present. + pub fn has_pack_cache(&self) -> bool { + self.pack_cache.is_some() + } + /// Remove the current pack cache as well as its constructor from this instance. + pub fn unset_pack_cache(&mut self) { + self.pack_cache = None; + self.new_pack_cache = None; + } + /// Remove the current object cache as well as its constructor from this instance. + pub fn unset_object_cache(&mut self) { + self.object_cache = None; + self.new_object_cache = None; + } +} + +impl<S> From<S> for Cache<S> +where + S: gix_pack::Find, +{ + fn from(store: S) -> Self { + Self { + inner: store, + pack_cache: None, + new_pack_cache: None, + object_cache: None, + new_object_cache: None, + } + } +} + +impl<S: Clone> Clone for Cache<S> { + fn clone(&self) -> Self { + Cache { + inner: self.inner.clone(), + new_pack_cache: self.new_pack_cache.clone(), + new_object_cache: self.new_object_cache.clone(), + pack_cache: self.new_pack_cache.as_ref().map(|create| RefCell::new(create())), + object_cache: self.new_object_cache.as_ref().map(|create| RefCell::new(create())), + } + } +} + +impl<S> Deref for Cache<S> { + type Target = S; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<S> DerefMut for Cache<S> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +mod impls { + use std::{io::Read, ops::DerefMut}; + + use gix_hash::{oid, ObjectId}; + use gix_object::{Data, Kind}; + use gix_pack::cache::Object; + + use crate::{find::Header, pack::data::entry::Location, Cache}; + + impl<S> crate::Write for Cache<S> + where + S: crate::Write, + { + type Error = S::Error; + + fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> { + self.inner.write_stream(kind, size, from) + } + } + + impl<S> crate::Find for Cache<S> + where + S: gix_pack::Find, + { + type Error = S::Error; + + fn contains(&self, id: impl AsRef<oid>) -> bool { + self.inner.contains(id) + } + + fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> { + gix_pack::Find::try_find(self, id, buffer).map(|t| t.map(|t| t.0)) + } + } + + impl<S> crate::Header for Cache<S> + where + S: crate::Header, + { + type Error = S::Error; + + fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> { + self.inner.try_header(id) + } + } + + impl<S> gix_pack::Find for Cache<S> + where + S: gix_pack::Find, + { + type Error = S::Error; + + fn contains(&self, id: impl AsRef<oid>) -> bool { + self.inner.contains(id) + } + + fn try_find<'a>( + &self, + id: impl AsRef<oid>, + buffer: &'a mut Vec<u8>, + ) -> Result<Option<(Data<'a>, Option<Location>)>, Self::Error> { + match self.pack_cache.as_ref().map(|rc| rc.borrow_mut()) { + Some(mut pack_cache) => self.try_find_cached(id, buffer, pack_cache.deref_mut()), + None => self.try_find_cached(id, buffer, &mut gix_pack::cache::Never), + } + } + + fn try_find_cached<'a>( + &self, + id: impl AsRef<oid>, + buffer: &'a mut Vec<u8>, + pack_cache: &mut impl gix_pack::cache::DecodeEntry, + ) -> Result<Option<(Data<'a>, Option<gix_pack::data::entry::Location>)>, Self::Error> { + if let Some(mut obj_cache) = self.object_cache.as_ref().map(|rc| rc.borrow_mut()) { + if let Some(kind) = obj_cache.get(&id.as_ref().to_owned(), buffer) { + return Ok(Some((Data::new(kind, buffer), None))); + } + } + let possibly_obj = self.inner.try_find_cached(id.as_ref(), buffer, pack_cache)?; + if let (Some(mut obj_cache), Some((obj, _location))) = + (self.object_cache.as_ref().map(|rc| rc.borrow_mut()), &possibly_obj) + { + obj_cache.put(id.as_ref().to_owned(), obj.kind, obj.data); + } + Ok(possibly_obj) + } + + fn location_by_oid(&self, id: impl AsRef<oid>, buf: &mut Vec<u8>) -> Option<gix_pack::data::entry::Location> { + self.inner.location_by_oid(id, buf) + } + + fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(u64, gix_hash::ObjectId)>> { + self.inner.pack_offsets_and_oid(pack_id) + } + + fn entry_by_location(&self, location: &Location) -> Option<gix_pack::find::Entry> { + self.inner.entry_by_location(location) + } + } +} |