gix_odb/store_impls/dynamic/
prefix.rs1use std::{collections::HashSet, ops::Deref};
2
3use gix_object::Exists;
4
5use crate::store::{load_index, Handle};
6
7pub mod lookup {
9 use crate::loose;
10
11 #[derive(thiserror::Error, Debug)]
13 #[allow(missing_docs)]
14 pub enum Error {
15 #[error("An error occurred looking up a prefix which requires iteration")]
16 LooseWalkDir(#[from] loose::iter::Error),
17 #[error(transparent)]
18 LoadIndex(#[from] crate::store::load_index::Error),
19 }
20
21 pub type Outcome = Result<gix_hash::ObjectId, ()>;
24}
25
26pub mod disambiguate {
28 #[derive(Debug, Copy, Clone)]
30 pub struct Candidate {
31 id: gix_hash::ObjectId,
32 hex_len: usize,
33 }
34
35 impl Candidate {
36 pub fn new(id: impl Into<gix_hash::ObjectId>, hex_len: usize) -> Result<Self, gix_hash::prefix::Error> {
41 let id = id.into();
42 gix_hash::Prefix::new(&id, hex_len)?;
43 Ok(Candidate { id, hex_len })
44 }
45
46 pub fn to_prefix(&self) -> gix_hash::Prefix {
48 gix_hash::Prefix::new(&self.id, self.hex_len).expect("our hex-len to always be in bounds")
49 }
50
51 pub(crate) fn inc_hex_len(&mut self) {
52 self.hex_len += 1;
53 assert!(self.hex_len <= self.id.kind().len_in_hex());
54 }
55
56 pub(crate) fn id(&self) -> &gix_hash::oid {
57 &self.id
58 }
59
60 pub(crate) fn hex_len(&self) -> usize {
61 self.hex_len
62 }
63 }
64
65 #[derive(thiserror::Error, Debug)]
67 #[allow(missing_docs)]
68 pub enum Error {
69 #[error("An error occurred while trying to determine if a full hash contained in the object database")]
70 Contains(#[from] crate::store::find::Error),
71 #[error(transparent)]
72 Lookup(#[from] super::lookup::Error),
73 }
74}
75
76impl<S> Handle<S>
77where
78 S: Deref<Target = super::Store> + Clone,
79{
80 pub fn packed_object_count(&self) -> Result<u64, load_index::Error> {
83 let mut count = self.packed_object_count.borrow_mut();
84 match *count {
85 Some(count) => Ok(count),
86 None => {
87 let _span = gix_features::trace::detail!("gix_odb::Handle::packed_object_count()");
88 let mut snapshot = self.snapshot.borrow_mut();
89 *snapshot = self.store.load_all_indices()?;
90 let mut obj_count = 0;
91 for index in &snapshot.indices {
92 obj_count += u64::from(index.num_objects());
93 }
94 *count = Some(obj_count);
95 Ok(obj_count)
96 }
97 }
98 }
99
100 pub fn disambiguate_prefix(
104 &self,
105 mut candidate: disambiguate::Candidate,
106 ) -> Result<Option<gix_hash::Prefix>, disambiguate::Error> {
107 let max_hex_len = candidate.id().kind().len_in_hex();
108 if candidate.hex_len() == max_hex_len {
109 return Ok(self.exists(candidate.id()).then(|| candidate.to_prefix()));
110 }
111
112 while candidate.hex_len() != max_hex_len {
113 let res = self.lookup_prefix(candidate.to_prefix(), None)?;
114 match res {
115 Some(Ok(_id)) => return Ok(Some(candidate.to_prefix())),
116 Some(Err(())) => {
117 candidate.inc_hex_len();
118 continue;
119 }
120 None => return Ok(None),
121 }
122 }
123 Ok(Some(candidate.to_prefix()))
124 }
125
126 pub fn lookup_prefix(
143 &self,
144 prefix: gix_hash::Prefix,
145 mut candidates: Option<&mut HashSet<gix_hash::ObjectId>>,
146 ) -> Result<Option<lookup::Outcome>, lookup::Error> {
147 let mut candidate: Option<gix_hash::ObjectId> = None;
148 loop {
149 let snapshot = self.snapshot.borrow();
150 for index in &snapshot.indices {
151 #[allow(clippy::needless_option_as_deref)] let lookup_result = index.lookup_prefix(prefix, candidates.as_deref_mut());
153 if candidates.is_none() && !check_candidate(lookup_result, &mut candidate) {
154 return Ok(Some(Err(())));
155 }
156 }
157
158 for lodb in snapshot.loose_dbs.iter() {
159 #[allow(clippy::needless_option_as_deref)] let lookup_result = lodb.lookup_prefix(prefix, candidates.as_deref_mut())?;
161 if candidates.is_none() && !check_candidate(lookup_result, &mut candidate) {
162 return Ok(Some(Err(())));
163 }
164 }
165
166 match self.store.load_one_index(self.refresh, snapshot.marker)? {
167 Some(new_snapshot) => {
168 drop(snapshot);
169 *self.snapshot.borrow_mut() = new_snapshot;
170 }
171 None => {
172 return match &candidates {
173 Some(candidates) => match candidates.len() {
174 0 => Ok(None),
175 1 => Ok(candidates.iter().copied().next().map(Ok)),
176 _ => Ok(Some(Err(()))),
177 },
178 None => Ok(candidate.map(Ok)),
179 };
180 }
181 }
182 }
183
184 fn check_candidate(lookup_result: Option<lookup::Outcome>, candidate: &mut Option<gix_hash::ObjectId>) -> bool {
185 match (lookup_result, &*candidate) {
186 (Some(Ok(oid)), Some(candidate)) if *candidate != oid => false,
187 (Some(Ok(_)) | None, Some(_)) | (None, None) => true,
188 (Some(Err(())), _) => false,
189 (Some(Ok(oid)), None) => {
190 *candidate = Some(oid);
191 true
192 }
193 }
194 }
195 }
196}