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