git_odb/store_impls/dynamic/
handle.rs1use std::{
2 cell::RefCell,
3 convert::{TryFrom, TryInto},
4 ops::Deref,
5 rc::Rc,
6 sync::{atomic::Ordering, Arc},
7};
8
9use git_features::threading::OwnShared;
10use git_hash::oid;
11
12use crate::store::{handle, types, RefreshMode};
13
14pub(crate) enum SingleOrMultiIndex {
15 Single {
16 index: Arc<git_pack::index::File>,
17 data: Option<Arc<git_pack::data::File>>,
18 },
19 Multi {
20 index: Arc<git_pack::multi_index::File>,
21 data: Vec<Option<Arc<git_pack::data::File>>>,
22 },
23}
24
25pub(crate) enum IntraPackLookup<'a> {
27 Single(&'a git_pack::index::File),
28 Multi {
31 index: &'a git_pack::multi_index::File,
32 required_pack_index: git_pack::multi_index::PackIndex,
33 },
34}
35
36impl<'a> IntraPackLookup<'a> {
37 pub(crate) fn pack_offset_by_id(&self, id: &oid) -> Option<git_pack::data::Offset> {
38 match self {
39 IntraPackLookup::Single(index) => index
40 .lookup(id)
41 .map(|entry_index| index.pack_offset_at_index(entry_index)),
42 IntraPackLookup::Multi {
43 index,
44 required_pack_index,
45 } => index.lookup(id).and_then(|entry_index| {
46 let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(entry_index);
47 (pack_index == *required_pack_index).then_some(pack_offset)
48 }),
49 }
50 }
51}
52
53pub struct IndexLookup {
54 pub(crate) file: SingleOrMultiIndex,
55 pub(crate) id: types::IndexId,
57}
58
59pub struct IndexForObjectInPack {
60 pub(crate) pack_id: types::PackId,
62 pub(crate) pack_offset: u64,
64}
65
66pub(crate) mod index_lookup {
67 use std::{collections::HashSet, sync::Arc};
68
69 use git_hash::oid;
70
71 use crate::store::{handle, handle::IntraPackLookup, types};
72
73 pub(crate) struct Outcome<'a> {
74 pub object_index: handle::IndexForObjectInPack,
75 pub index_file: IntraPackLookup<'a>,
76 pub pack: &'a mut Option<Arc<git_pack::data::File>>,
77 }
78
79 impl handle::IndexLookup {
80 pub(crate) fn iter(
83 &self,
84 pack_id: types::PackId,
85 ) -> Option<Box<dyn Iterator<Item = git_pack::index::Entry> + '_>> {
86 (self.id == pack_id.index).then(|| match &self.file {
87 handle::SingleOrMultiIndex::Single { index, .. } => index.iter(),
88 handle::SingleOrMultiIndex::Multi { index, .. } => {
89 let pack_index = pack_id.multipack_index.expect(
90 "BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable",
91 );
92 Box::new(index.iter().filter_map(move |e| {
93 (e.pack_index == pack_index).then_some(git_pack::index::Entry {
94 oid: e.oid,
95 pack_offset: e.pack_offset,
96 crc32: None,
97 })
98 }))
99 }
100 })
101 }
102
103 pub(crate) fn pack(&mut self, pack_id: types::PackId) -> Option<&'_ mut Option<Arc<git_pack::data::File>>> {
104 (self.id == pack_id.index).then(move || match &mut self.file {
105 handle::SingleOrMultiIndex::Single { data, .. } => data,
106 handle::SingleOrMultiIndex::Multi { data, .. } => {
107 let pack_index = pack_id.multipack_index.expect(
108 "BUG: multi-pack index must be set if this is a multi-pack, pack-indices seem unstable",
109 );
110 &mut data[pack_index as usize]
111 }
112 })
113 }
114
115 pub(crate) fn contains(&self, object_id: &oid) -> bool {
117 match &self.file {
118 handle::SingleOrMultiIndex::Single { index, .. } => index.lookup(object_id).is_some(),
119 handle::SingleOrMultiIndex::Multi { index, .. } => index.lookup(object_id).is_some(),
120 }
121 }
122
123 pub(crate) fn oid_at_index(&self, entry_index: u32) -> &git_hash::oid {
125 match &self.file {
126 handle::SingleOrMultiIndex::Single { index, .. } => index.oid_at_index(entry_index),
127 handle::SingleOrMultiIndex::Multi { index, .. } => index.oid_at_index(entry_index),
128 }
129 }
130
131 pub(crate) fn num_objects(&self) -> u32 {
133 match &self.file {
134 handle::SingleOrMultiIndex::Single { index, .. } => index.num_objects(),
135 handle::SingleOrMultiIndex::Multi { index, .. } => index.num_objects(),
136 }
137 }
138
139 pub(crate) fn lookup_prefix(
141 &self,
142 prefix: git_hash::Prefix,
143 candidates: Option<&mut HashSet<git_hash::ObjectId>>,
144 ) -> Option<crate::store::prefix::lookup::Outcome> {
145 let mut candidate_entries = candidates.as_ref().map(|_| 0..0);
146 let res = match &self.file {
147 handle::SingleOrMultiIndex::Single { index, .. } => {
148 index.lookup_prefix(prefix, candidate_entries.as_mut())
149 }
150 handle::SingleOrMultiIndex::Multi { index, .. } => {
151 index.lookup_prefix(prefix, candidate_entries.as_mut())
152 }
153 }?;
154
155 if let Some((candidates, entries)) = candidates.zip(candidate_entries) {
156 candidates.extend(entries.map(|entry| self.oid_at_index(entry).to_owned()));
157 }
158 Some(res.map(|entry_index| self.oid_at_index(entry_index).to_owned()))
159 }
160
161 pub(crate) fn lookup(&mut self, object_id: &oid) -> Option<Outcome<'_>> {
167 let id = self.id;
168 match &mut self.file {
169 handle::SingleOrMultiIndex::Single { index, data } => index.lookup(object_id).map(move |idx| Outcome {
170 object_index: handle::IndexForObjectInPack {
171 pack_id: types::PackId {
172 index: id,
173 multipack_index: None,
174 },
175 pack_offset: index.pack_offset_at_index(idx),
176 },
177 index_file: IntraPackLookup::Single(index),
178 pack: data,
179 }),
180 handle::SingleOrMultiIndex::Multi { index, data } => index.lookup(object_id).map(move |idx| {
181 let (pack_index, pack_offset) = index.pack_id_and_pack_offset_at_index(idx);
182 Outcome {
183 object_index: handle::IndexForObjectInPack {
184 pack_id: types::PackId {
185 index: id,
186 multipack_index: Some(pack_index),
187 },
188 pack_offset,
189 },
190 index_file: IntraPackLookup::Multi {
191 index,
192 required_pack_index: pack_index,
193 },
194 pack: &mut data[pack_index as usize],
195 }
196 }),
197 }
198 }
199 }
200}
201
202pub(crate) enum Mode {
203 DeletedPacksAreInaccessible,
204 KeepDeletedPacksAvailable,
206}
207
208impl super::Store {
210 pub(crate) fn register_handle(&self) -> Mode {
211 self.num_handles_unstable.fetch_add(1, Ordering::Relaxed);
212 Mode::DeletedPacksAreInaccessible
213 }
214 pub(crate) fn remove_handle(&self, mode: Mode) {
215 match mode {
216 Mode::KeepDeletedPacksAvailable => {
217 let _lock = self.write.lock();
218 self.num_handles_stable.fetch_sub(1, Ordering::SeqCst)
219 }
220 Mode::DeletedPacksAreInaccessible => self.num_handles_unstable.fetch_sub(1, Ordering::Relaxed),
221 };
222 }
223 pub(crate) fn upgrade_handle(&self, mode: Mode) -> Mode {
224 if let Mode::DeletedPacksAreInaccessible = mode {
225 let _lock = self.write.lock();
226 self.num_handles_stable.fetch_add(1, Ordering::SeqCst);
227 self.num_handles_unstable.fetch_sub(1, Ordering::SeqCst);
228 }
229 Mode::KeepDeletedPacksAvailable
230 }
231}
232
233impl super::Store {
235 pub const INITIAL_MAX_RECURSION_DEPTH: usize = 32;
237
238 pub fn to_cache(self: &OwnShared<Self>) -> crate::Cache<super::Handle<OwnShared<super::Store>>> {
242 self.to_handle().into()
243 }
244
245 pub fn to_cache_arc(self: &Arc<Self>) -> crate::Cache<super::Handle<Arc<super::Store>>> {
247 self.to_handle_arc().into()
248 }
249
250 pub fn to_handle(self: &OwnShared<Self>) -> super::Handle<OwnShared<super::Store>> {
254 let token = self.register_handle();
255 super::Handle {
256 store: self.clone(),
257 refresh: RefreshMode::default(),
258 ignore_replacements: false,
259 token: Some(token),
260 snapshot: RefCell::new(self.collect_snapshot()),
261 max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
262 packed_object_count: Default::default(),
263 }
264 }
265
266 pub fn to_handle_arc(self: &Arc<Self>) -> super::Handle<Arc<super::Store>> {
270 let token = self.register_handle();
271 super::Handle {
272 store: self.clone(),
273 refresh: Default::default(),
274 ignore_replacements: false,
275 token: Some(token),
276 snapshot: RefCell::new(self.collect_snapshot()),
277 max_recursion_depth: Self::INITIAL_MAX_RECURSION_DEPTH,
278 packed_object_count: Default::default(),
279 }
280 }
281
282 pub fn into_shared_arc(self: OwnShared<Self>) -> Arc<Self> {
288 match OwnShared::try_unwrap(self) {
289 Ok(this) => Arc::new(this),
290 Err(_) => panic!("BUG: Must be called when there is only one owner for this RC"),
291 }
292 }
293}
294
295impl<S> super::Handle<S>
296where
297 S: Deref<Target = super::Store> + Clone,
298{
299 pub fn prevent_pack_unload(&mut self) {
303 self.token = self.token.take().map(|token| self.store.upgrade_handle(token));
304 }
305
306 pub fn store_ref(&self) -> &S::Target {
308 &self.store
309 }
310
311 pub fn store(&self) -> S {
313 self.store.clone()
314 }
315
316 pub fn refresh_never(&mut self) {
322 self.refresh = RefreshMode::Never;
323 }
324
325 pub fn refresh_mode(&mut self) -> RefreshMode {
327 self.refresh
328 }
329}
330
331impl<S> Drop for super::Handle<S>
332where
333 S: Deref<Target = super::Store> + Clone,
334{
335 fn drop(&mut self) {
336 if let Some(token) = self.token.take() {
337 self.store.remove_handle(token)
338 }
339 }
340}
341
342impl TryFrom<&super::Store> for super::Store {
343 type Error = std::io::Error;
344
345 fn try_from(s: &super::Store) -> Result<Self, Self::Error> {
346 super::Store::at_opts(
347 s.path(),
348 s.replacements(),
349 crate::store::init::Options {
350 slots: crate::store::init::Slots::Given(s.files.len().try_into().expect("BUG: too many slots")),
351 object_hash: Default::default(),
352 use_multi_pack_index: false,
353 current_dir: s.current_dir.clone().into(),
354 },
355 )
356 }
357}
358
359impl super::Handle<Rc<super::Store>> {
360 pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> {
362 let store = Arc::new(super::Store::try_from(self.store_ref())?);
363 let mut cache = store.to_handle_arc();
364 cache.refresh = self.refresh;
365 cache.max_recursion_depth = self.max_recursion_depth;
366 Ok(cache)
367 }
368}
369
370impl super::Handle<Arc<super::Store>> {
371 pub fn into_arc(self) -> std::io::Result<super::Handle<Arc<super::Store>>> {
373 Ok(self)
374 }
375}
376
377impl<S> Clone for super::Handle<S>
378where
379 S: Deref<Target = super::Store> + Clone,
380{
381 fn clone(&self) -> Self {
382 super::Handle {
383 store: self.store.clone(),
384 refresh: self.refresh,
385 ignore_replacements: self.ignore_replacements,
386 token: {
387 let token = self.store.register_handle();
388 match self.token.as_ref().expect("token is always set here ") {
389 handle::Mode::DeletedPacksAreInaccessible => token,
390 handle::Mode::KeepDeletedPacksAvailable => self.store.upgrade_handle(token),
391 }
392 .into()
393 },
394 snapshot: RefCell::new(self.store.collect_snapshot()),
395 max_recursion_depth: self.max_recursion_depth,
396 packed_object_count: Default::default(),
397 }
398 }
399}