git_odb/store_impls/dynamic/
prefix.rs1use std::{collections::HashSet, ops::Deref};
2
3use crate::{
4 store::{load_index, Handle},
5 Find,
6};
7
8pub mod lookup {
10 use crate::loose;
11
12 #[derive(thiserror::Error, Debug)]
14 #[allow(missing_docs)]
15 pub enum Error {
16 #[error("An error occurred looking up a prefix which requires iteration")]
17 LooseWalkDir(#[from] loose::iter::Error),
18 #[error(transparent)]
19 LoadIndex(#[from] crate::store::load_index::Error),
20 }
21
22 pub type Outcome = Result<git_hash::ObjectId, ()>;
25}
26
27pub mod disambiguate {
29 #[derive(Debug, Copy, Clone)]
31 pub struct Candidate {
32 id: git_hash::ObjectId,
33 hex_len: usize,
34 }
35
36 impl Candidate {
37 pub fn new(id: impl Into<git_hash::ObjectId>, hex_len: usize) -> Result<Self, git_hash::prefix::Error> {
42 let id = id.into();
43 git_hash::Prefix::new(id, hex_len)?;
44 Ok(Candidate { id, hex_len })
45 }
46
47 pub fn to_prefix(&self) -> git_hash::Prefix {
49 git_hash::Prefix::new(self.id, self.hex_len).expect("our hex-len to always be in bounds")
50 }
51
52 pub(crate) fn inc_hex_len(&mut self) {
53 self.hex_len += 1;
54 assert!(self.hex_len <= self.id.kind().len_in_hex());
55 }
56
57 pub(crate) fn id(&self) -> &git_hash::oid {
58 &self.id
59 }
60
61 pub(crate) fn hex_len(&self) -> usize {
62 self.hex_len
63 }
64 }
65
66 #[derive(thiserror::Error, Debug)]
68 #[allow(missing_docs)]
69 pub enum Error {
70 #[error("An error occurred while trying to determine if a full hash contained in the object database")]
71 Contains(#[from] crate::store::find::Error),
72 #[error(transparent)]
73 Lookup(#[from] super::lookup::Error),
74 }
75}
76
77impl<S> Handle<S>
78where
79 S: Deref<Target = super::Store> + Clone,
80{
81 pub fn packed_object_count(&self) -> Result<u64, load_index::Error> {
84 let mut count = self.packed_object_count.borrow_mut();
85 match *count {
86 Some(count) => Ok(count),
87 None => {
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 += index.num_objects() as u64;
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<git_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.contains(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: git_hash::Prefix,
145 mut candidates: Option<&mut HashSet<git_hash::ObjectId>>,
146 ) -> Result<Option<lookup::Outcome>, lookup::Error> {
147 let mut candidate: Option<git_hash::ObjectId> = None;
148 loop {
149 let snapshot = self.snapshot.borrow();
150 for index in snapshot.indices.iter() {
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().cloned().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<git_hash::ObjectId>) -> bool {
185 match (lookup_result, &*candidate) {
186 (Some(Ok(oid)), Some(candidate)) if *candidate != oid => false,
187 (Some(Ok(_)), Some(_)) | (None, None) | (None, Some(_)) => true,
188 (Some(Err(())), _) => false,
189 (Some(Ok(oid)), None) => {
190 *candidate = Some(oid);
191 true
192 }
193 }
194 }
195 }
196}