Skip to main content

gix_odb/
memory.rs

1use std::{
2    cell::RefCell,
3    ops::{Deref, DerefMut},
4    rc::Rc,
5    sync::Arc,
6};
7
8use gix_object::Data;
9
10use crate::{find::Header, Cache};
11
12/// An object database to read from any implementation but write to memory.
13/// Previously written objects can be returned from memory upon query, which makes the view of objects consistent.
14/// In-Memory objects can be disabled by [taking out its storage](Proxy::take_object_memory). From there in-memory
15/// object can also be persisted one by one.
16///
17/// It's possible to turn off the memory by removing it from the instance.
18pub struct Proxy<T> {
19    /// The actual odb implementation
20    inner: T,
21    /// The kind of hash to produce when writing new objects.
22    object_hash: gix_hash::Kind,
23    /// The storage for in-memory objects.
24    /// If `None`, the proxy will always read from and write-through to `inner`.
25    memory: Option<RefCell<Storage>>,
26}
27
28/// Lifecycle
29impl<T> Proxy<T> {
30    /// Create a new instance using `odb` as actual object provider, with an empty in-memory store for
31    /// objects that are to be written.
32    /// Use `object_hash` to determine the kind of hash to produce when writing new objects.
33    pub fn new(odb: T, object_hash: gix_hash::Kind) -> Proxy<T> {
34        Proxy {
35            inner: odb,
36            object_hash,
37            memory: Some(Default::default()),
38        }
39    }
40
41    /// Turn ourselves into our inner object database, while deallocating objects stored in memory.
42    pub fn into_inner(self) -> T {
43        self.inner
44    }
45
46    /// Strip object memory off this instance, which means that writes will go through to the inner object database
47    /// right away.
48    /// This mode makes the proxy fully transparent.
49    pub fn with_write_passthrough(mut self) -> Self {
50        self.memory.take();
51        self
52    }
53}
54
55impl Proxy<Cache<crate::store::Handle<Arc<crate::Store>>>> {
56    /// No op, as we are containing an arc handle already.
57    pub fn into_arc(self) -> std::io::Result<Proxy<Cache<crate::store::Handle<Arc<crate::Store>>>>> {
58        Ok(self)
59    }
60}
61
62impl Proxy<Cache<crate::store::Handle<Rc<crate::Store>>>> {
63    /// Create an entirely new instance, but with the in-memory objects moving between them.
64    pub fn into_arc(self) -> std::io::Result<Proxy<Cache<crate::store::Handle<Arc<crate::Store>>>>> {
65        Ok(Proxy {
66            inner: self.inner.into_arc()?,
67            object_hash: self.object_hash,
68            memory: self.memory,
69        })
70    }
71}
72
73impl From<crate::Handle> for Proxy<crate::Handle> {
74    fn from(odb: crate::Handle) -> Self {
75        let object_hash = odb.store.object_hash;
76        Proxy::new(odb, object_hash)
77    }
78}
79
80/// Memory Access
81impl<T> Proxy<T> {
82    /// Take all the objects in memory so far, with the memory storage itself and return it.
83    ///
84    /// The instance will remain in a state where it won't be able to store objects in memory at all,
85    /// they will now be stored in the underlying object database.
86    /// This mode makes the proxy fully transparent.
87    ///
88    /// To avoid that, use [`reset_object_memory()`](Self::reset_object_memory()) or return the storage
89    /// using [`set_object_memory()`](Self::set_object_memory()).
90    pub fn take_object_memory(&mut self) -> Option<Storage> {
91        self.memory.take().map(RefCell::into_inner)
92    }
93
94    /// Set the object storage to contain only `new` objects, and return whichever objects were there previously.
95    pub fn set_object_memory(&mut self, new: Storage) -> Option<Storage> {
96        let previous = self.take_object_memory();
97        self.memory = Some(RefCell::new(new));
98        previous
99    }
100
101    /// If objects aren't written to memory yet, this will happen after the call.
102    ///
103    /// Otherwise, no change will be performed.
104    pub fn enable_object_memory(&mut self) -> &mut Self {
105        if self.memory.is_none() {
106            self.memory = Some(Default::default());
107        }
108        self
109    }
110
111    /// Reset the internal storage to be empty, and return the previous storage, with all objects
112    /// it contained.
113    ///
114    /// Note that this does nothing if this instance didn't contain object memory in the first place.
115    /// In that case, set it explicitly.
116    pub fn reset_object_memory(&self) -> Option<Storage> {
117        self.memory.as_ref().map(|m| std::mem::take(&mut *m.borrow_mut()))
118    }
119
120    /// Return the amount of objects currently stored in memory.
121    pub fn num_objects_in_memory(&self) -> usize {
122        self.memory.as_ref().map_or(0, |m| m.borrow().len())
123    }
124}
125
126impl<T> Clone for Proxy<T>
127where
128    T: Clone,
129{
130    fn clone(&self) -> Self {
131        Proxy {
132            inner: self.inner.clone(),
133            object_hash: self.object_hash,
134            memory: self.memory.clone(),
135        }
136    }
137}
138
139impl<T> gix_object::Find for Proxy<T>
140where
141    T: gix_object::Find,
142{
143    fn try_find<'a>(
144        &self,
145        id: &gix_hash::oid,
146        buffer: &'a mut Vec<u8>,
147    ) -> Result<Option<Data<'a>>, gix_object::find::Error> {
148        if let Some(map) = self.memory.as_ref() {
149            let map = map.borrow();
150            if let Some((kind, data)) = map.get(id) {
151                buffer.clear();
152                buffer.extend_from_slice(data);
153                return Ok(Some(Data {
154                    kind: *kind,
155                    hash_kind: id.kind(),
156                    data: &*buffer,
157                }));
158            }
159        }
160        self.inner.try_find(id, buffer)
161    }
162}
163
164impl<T> gix_object::Exists for Proxy<T>
165where
166    T: gix_object::Exists,
167{
168    fn exists(&self, id: &gix_hash::oid) -> bool {
169        self.memory.as_ref().is_some_and(|map| map.borrow().contains_key(id)) || self.inner.exists(id)
170    }
171}
172
173impl<T> crate::Header for Proxy<T>
174where
175    T: crate::Header,
176{
177    fn try_header(&self, id: &gix_hash::oid) -> Result<Option<Header>, gix_object::find::Error> {
178        if let Some(map) = self.memory.as_ref() {
179            let map = map.borrow();
180            if let Some((kind, data)) = map.get(id) {
181                return Ok(Some(Header::Loose {
182                    kind: *kind,
183                    size: data.len() as u64,
184                }));
185            }
186        }
187        self.inner.try_header(id)
188    }
189}
190
191impl<T> gix_object::FindHeader for Proxy<T>
192where
193    T: gix_object::FindHeader,
194{
195    fn try_header(&self, id: &gix_hash::oid) -> Result<Option<gix_object::Header>, gix_object::find::Error> {
196        if let Some(map) = self.memory.as_ref() {
197            let map = map.borrow();
198            if let Some((kind, data)) = map.get(id) {
199                return Ok(Some(gix_object::Header {
200                    kind: *kind,
201                    size: data.len() as u64,
202                }));
203            }
204        }
205        self.inner.try_header(id)
206    }
207}
208
209impl<T> gix_object::Write for Proxy<T>
210where
211    T: gix_object::Write,
212{
213    fn write_stream(
214        &self,
215        kind: gix_object::Kind,
216        size: u64,
217        from: &mut dyn std::io::Read,
218    ) -> Result<gix_hash::ObjectId, gix_object::write::Error> {
219        let Some(map) = self.memory.as_ref() else {
220            return self.inner.write_stream(kind, size, from);
221        };
222
223        let mut buf = Vec::new();
224        from.read_to_end(&mut buf)?;
225
226        let id = gix_object::compute_hash(self.object_hash, kind, &buf)?;
227        map.borrow_mut().insert(id, (kind, buf));
228        Ok(id)
229    }
230}
231
232impl<T> Deref for Proxy<T> {
233    type Target = T;
234
235    fn deref(&self) -> &Self::Target {
236        &self.inner
237    }
238}
239
240impl<T> DerefMut for Proxy<T> {
241    fn deref_mut(&mut self) -> &mut Self::Target {
242        &mut self.inner
243    }
244}
245
246/// A mapping between an object id and all data corresponding to an object, acting like a `HashMap<ObjectID, (Kind, Data)>`.
247#[derive(Default, Debug, Clone, Eq, PartialEq)]
248pub struct Storage(gix_hashtable::HashMap<gix_hash::ObjectId, (gix_object::Kind, Vec<u8>)>);
249
250impl Deref for Storage {
251    type Target = gix_hashtable::HashMap<gix_hash::ObjectId, (gix_object::Kind, Vec<u8>)>;
252
253    fn deref(&self) -> &Self::Target {
254        &self.0
255    }
256}
257
258impl DerefMut for Storage {
259    fn deref_mut(&mut self) -> &mut Self::Target {
260        &mut self.0
261    }
262}