Skip to main content

gix_odb/
cache.rs

1use std::{
2    cell::RefCell,
3    ops::{Deref, DerefMut},
4    rc::Rc,
5    sync::Arc,
6};
7
8use crate::Cache;
9
10/// A type to store pack caches in boxes.
11pub type PackCache = dyn gix_pack::cache::DecodeEntry + Send + 'static;
12/// A constructor for boxed pack caches.
13pub type NewPackCacheFn = dyn Fn() -> Box<PackCache> + Send + Sync + 'static;
14
15/// A type to store object caches in boxes.
16pub type ObjectCache = dyn gix_pack::cache::Object + Send + 'static;
17/// A constructor for boxed object caches.
18pub type NewObjectCacheFn = dyn Fn() -> Box<ObjectCache> + Send + Sync + 'static;
19
20impl Cache<crate::store::Handle<Rc<crate::Store>>> {
21    /// Convert this cache's handle into one that keeps its store in an arc. This creates an entirely new store,
22    /// so should be done early to avoid unnecessary work (and mappings).
23    pub fn into_arc(self) -> std::io::Result<Cache<crate::store::Handle<Arc<crate::Store>>>> {
24        let inner = self.inner.into_arc()?;
25        Ok(Cache {
26            inner,
27            new_pack_cache: self.new_pack_cache,
28            new_object_cache: self.new_object_cache,
29            pack_cache: self.pack_cache,
30            object_cache: self.object_cache,
31        })
32    }
33}
34impl Cache<crate::store::Handle<Arc<crate::Store>>> {
35    /// No op, as we are containing an arc handle already.
36    pub fn into_arc(self) -> std::io::Result<Cache<crate::store::Handle<Arc<crate::Store>>>> {
37        Ok(self)
38    }
39}
40
41impl<S> Cache<S> {
42    /// Dissolve this instance, discard all caches, and return the inner implementation.
43    pub fn into_inner(self) -> S {
44        self.inner
45    }
46    /// Use this methods directly after creating a new instance to add a constructor for pack caches.
47    ///
48    /// These are used to speed up decoding objects which are located in packs, reducing long delta chains by storing
49    /// their intermediate results.
50    pub fn with_pack_cache(mut self, create: impl Fn() -> Box<PackCache> + Send + Sync + 'static) -> Self {
51        self.pack_cache = Some(RefCell::new(create()));
52        self.new_pack_cache = Some(Arc::new(create));
53        self
54    }
55    /// Use this methods directly after creating a new instance to add a constructor for object caches.
56    ///
57    /// Only use this kind of cache if the same objects are repeatedly accessed for great speedups, usually during diffing of
58    /// trees.
59    pub fn with_object_cache(mut self, create: impl Fn() -> Box<ObjectCache> + Send + Sync + 'static) -> Self {
60        self.object_cache = Some(RefCell::new(create()));
61        self.new_object_cache = Some(Arc::new(create));
62        self
63    }
64    /// Set the pack cache constructor on this instance.
65    pub fn set_pack_cache(&mut self, create: impl Fn() -> Box<PackCache> + Send + Sync + 'static) {
66        self.pack_cache = Some(RefCell::new(create()));
67        self.new_pack_cache = Some(Arc::new(create));
68    }
69    /// Set the object cache constructor on this instance.
70    pub fn set_object_cache(&mut self, create: impl Fn() -> Box<ObjectCache> + Send + Sync + 'static) {
71        self.object_cache = Some(RefCell::new(create()));
72        self.new_object_cache = Some(Arc::new(create));
73    }
74    /// Return true if an object cache is present.
75    pub fn has_object_cache(&self) -> bool {
76        self.object_cache.is_some()
77    }
78    /// Return true if a pack cache is present.
79    pub fn has_pack_cache(&self) -> bool {
80        self.pack_cache.is_some()
81    }
82    /// Remove the current pack cache as well as its constructor from this instance.
83    pub fn unset_pack_cache(&mut self) {
84        self.pack_cache = None;
85        self.new_pack_cache = None;
86    }
87    /// Remove the current object cache as well as its constructor from this instance.
88    pub fn unset_object_cache(&mut self) {
89        self.object_cache = None;
90        self.new_object_cache = None;
91    }
92}
93
94impl<S> From<S> for Cache<S>
95where
96    S: gix_pack::Find,
97{
98    fn from(store: S) -> Self {
99        Self {
100            inner: store,
101            pack_cache: None,
102            new_pack_cache: None,
103            object_cache: None,
104            new_object_cache: None,
105        }
106    }
107}
108
109impl<S: Clone> Clone for Cache<S> {
110    fn clone(&self) -> Self {
111        Cache {
112            inner: self.inner.clone(),
113            new_pack_cache: self.new_pack_cache.clone(),
114            new_object_cache: self.new_object_cache.clone(),
115            pack_cache: self.new_pack_cache.as_ref().map(|create| RefCell::new(create())),
116            object_cache: self.new_object_cache.as_ref().map(|create| RefCell::new(create())),
117        }
118    }
119}
120
121impl<S> Deref for Cache<S> {
122    type Target = S;
123
124    fn deref(&self) -> &Self::Target {
125        &self.inner
126    }
127}
128
129impl<S> DerefMut for Cache<S> {
130    fn deref_mut(&mut self) -> &mut Self::Target {
131        &mut self.inner
132    }
133}
134
135mod impls {
136    use std::{cell::RefCell, io::Read, ops::DerefMut};
137
138    use gix_hash::{ObjectId, oid};
139    use gix_object::{Data, Kind};
140    use gix_pack::cache::Object;
141
142    use crate::{Cache, find::Header, pack::data::entry::Location};
143
144    impl<S> gix_object::Write for Cache<S>
145    where
146        S: gix_object::Write,
147    {
148        fn write_stream(
149            &self,
150            kind: Kind,
151            size: u64,
152            from: &mut dyn Read,
153        ) -> Result<ObjectId, gix_object::write::Error> {
154            self.inner.write_stream(kind, size, from)
155        }
156
157        fn write_buf_with_known_id(
158            &self,
159            kind: Kind,
160            from: &[u8],
161            id: ObjectId,
162        ) -> Result<ObjectId, gix_object::write::Error> {
163            self.inner.write_buf_with_known_id(kind, from, id)
164        }
165
166        fn write_stream_with_known_id(
167            &self,
168            kind: Kind,
169            size: u64,
170            from: &mut dyn Read,
171            id: ObjectId,
172        ) -> Result<ObjectId, gix_object::write::Error> {
173            self.inner.write_stream_with_known_id(kind, size, from, id)
174        }
175    }
176
177    impl<S> gix_object::Find for Cache<S>
178    where
179        S: gix_pack::Find,
180    {
181        fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, gix_object::find::Error> {
182            gix_pack::Find::try_find(self, id, buffer).map(|t| t.map(|t| t.0))
183        }
184    }
185
186    impl<S> gix_object::Exists for Cache<S>
187    where
188        S: gix_pack::Find,
189    {
190        fn exists(&self, id: &oid) -> bool {
191            self.inner.contains(id)
192        }
193    }
194
195    impl<S> crate::Header for Cache<S>
196    where
197        S: crate::Header,
198    {
199        fn try_header(&self, id: &oid) -> Result<Option<Header>, gix_object::find::Error> {
200            self.inner.try_header(id)
201        }
202    }
203
204    impl<S> gix_object::FindHeader for Cache<S>
205    where
206        S: gix_object::FindHeader,
207    {
208        fn try_header(&self, id: &oid) -> Result<Option<gix_object::Header>, gix_object::find::Error> {
209            self.inner.try_header(id)
210        }
211    }
212
213    impl<S> gix_pack::Find for Cache<S>
214    where
215        S: gix_pack::Find,
216    {
217        fn contains(&self, id: &oid) -> bool {
218            self.inner.contains(id)
219        }
220
221        fn try_find<'a>(
222            &self,
223            id: &oid,
224            buffer: &'a mut Vec<u8>,
225        ) -> Result<Option<(Data<'a>, Option<Location>)>, gix_object::find::Error> {
226            match self.pack_cache.as_ref().map(RefCell::borrow_mut) {
227                Some(mut pack_cache) => self.try_find_cached(id, buffer, pack_cache.deref_mut()),
228                None => self.try_find_cached(id, buffer, &mut gix_pack::cache::Never),
229            }
230        }
231
232        fn try_find_cached<'a>(
233            &self,
234            id: &oid,
235            buffer: &'a mut Vec<u8>,
236            pack_cache: &mut dyn gix_pack::cache::DecodeEntry,
237        ) -> Result<Option<(Data<'a>, Option<gix_pack::data::entry::Location>)>, gix_object::find::Error> {
238            if let Some(mut obj_cache) = self.object_cache.as_ref().map(RefCell::borrow_mut) {
239                if let Some(kind) = obj_cache.get(&id.as_ref().to_owned(), buffer) {
240                    return Ok(Some((Data::new(buffer, kind, id.kind()), None)));
241                }
242            }
243            let possibly_obj = self.inner.try_find_cached(id.as_ref(), buffer, pack_cache)?;
244            if let (Some(mut obj_cache), Some((obj, _location))) =
245                (self.object_cache.as_ref().map(RefCell::borrow_mut), &possibly_obj)
246            {
247                obj_cache.put(id.as_ref().to_owned(), obj.kind, obj.data);
248            }
249            Ok(possibly_obj)
250        }
251
252        fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<gix_pack::data::entry::Location> {
253            self.inner.location_by_oid(id, buf)
254        }
255
256        fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(u64, gix_hash::ObjectId)>> {
257            self.inner.pack_offsets_and_oid(pack_id)
258        }
259
260        fn entry_by_location(&self, location: &Location) -> Option<gix_pack::find::Entry> {
261            self.inner.entry_by_location(location)
262        }
263    }
264}