git_ref/store/packed/
find.rs1use std::convert::TryInto;
2
3use git_object::bstr::{BStr, BString, ByteSlice};
4
5use crate::{store_impl::packed, FullNameRef, PartialNameRef};
6
7impl packed::Buffer {
9 pub fn try_find<'a, Name, E>(&self, name: Name) -> Result<Option<packed::Reference<'_>>, Error>
14 where
15 Name: TryInto<&'a PartialNameRef, Error = E>,
16 Error: From<E>,
17 {
18 let name = name.try_into()?;
19 let mut buf = BString::default();
20 for inbetween in &["", "tags", "heads", "remotes"] {
21 let (name, was_absolute) = if name.looks_like_full_name() {
22 let name = FullNameRef::new_unchecked(name.as_bstr());
23 let name = match transform_full_name_for_lookup(name) {
24 None => return Ok(None),
25 Some(name) => name,
26 };
27 (name, true)
28 } else {
29 let full_name = name.construct_full_name_ref(true, inbetween, &mut buf);
30 (full_name, false)
31 };
32 match self.try_find_full_name(name)? {
33 Some(r) => return Ok(Some(r)),
34 None if was_absolute => return Ok(None),
35 None => continue,
36 }
37 }
38 Ok(None)
39 }
40
41 pub(crate) fn try_find_full_name(&self, name: &FullNameRef) -> Result<Option<packed::Reference<'_>>, Error> {
42 match self.binary_search_by(name.as_bstr()) {
43 Ok(line_start) => Ok(Some(
44 packed::decode::reference::<()>(&self.as_ref()[line_start..])
45 .map_err(|_| Error::Parse)?
46 .1,
47 )),
48 Err((parse_failure, _)) => {
49 if parse_failure {
50 Err(Error::Parse)
51 } else {
52 Ok(None)
53 }
54 }
55 }
56 }
57
58 pub fn find<'a, Name, E>(&self, name: Name) -> Result<packed::Reference<'_>, existing::Error>
60 where
61 Name: TryInto<&'a PartialNameRef, Error = E>,
62 Error: From<E>,
63 {
64 match self.try_find(name) {
65 Ok(Some(r)) => Ok(r),
66 Ok(None) => Err(existing::Error::NotFound),
67 Err(err) => Err(existing::Error::Find(err)),
68 }
69 }
70
71 pub(in crate::store_impl::packed) fn binary_search_by(&self, full_name: &BStr) -> Result<usize, (bool, usize)> {
74 let a = self.as_ref();
75 let search_start_of_record = |ofs: usize| {
76 a[..ofs]
77 .rfind(b"\n")
78 .and_then(|pos| {
79 let candidate = pos + 1;
80 a.get(candidate).and_then(|b| {
81 if *b == b'^' {
82 a[..pos].rfind(b"\n").map(|pos| pos + 1)
83 } else {
84 Some(candidate)
85 }
86 })
87 })
88 .unwrap_or(0)
89 };
90 let mut encountered_parse_failure = false;
91 a.binary_search_by_key(&full_name.as_ref(), |b: &u8| {
92 let ofs = b as *const u8 as usize - a.as_ptr() as usize;
93 let line = &a[search_start_of_record(ofs)..];
94 packed::decode::reference::<()>(line)
95 .map(|(_rest, r)| r.name.as_bstr().as_ref())
96 .map_err(|err| {
97 encountered_parse_failure = true;
98 err
99 })
100 .unwrap_or(&[])
101 })
102 .map(search_start_of_record)
103 .map_err(|pos| (encountered_parse_failure, search_start_of_record(pos)))
104 }
105}
106
107mod error {
108 use std::convert::Infallible;
109
110 #[derive(Debug, thiserror::Error)]
112 #[allow(missing_docs)]
113 pub enum Error {
114 #[error("The ref name or path is not a valid ref name")]
115 RefnameValidation(#[from] crate::name::Error),
116 #[error("The reference could not be parsed")]
117 Parse,
118 }
119
120 impl From<Infallible> for Error {
121 fn from(_: Infallible) -> Self {
122 unreachable!("this impl is needed to allow passing a known valid partial path as parameter")
123 }
124 }
125}
126pub use error::Error;
127
128pub mod existing {
130
131 #[derive(Debug, thiserror::Error)]
133 #[allow(missing_docs)]
134 pub enum Error {
135 #[error("The find operation failed")]
136 Find(#[from] super::Error),
137 #[error("The reference did not exist even though that was expected")]
138 NotFound,
139 }
140}
141
142pub(crate) fn transform_full_name_for_lookup(name: &FullNameRef) -> Option<&FullNameRef> {
143 match name.category_and_short_name() {
144 Some((c, sn)) => {
145 use crate::Category::*;
146 Some(match c {
147 MainRef | LinkedRef { .. } => FullNameRef::new_unchecked(sn),
148 Tag | RemoteBranch | LocalBranch | Bisect | Rewritten | Note => name,
149 MainPseudoRef | PseudoRef | LinkedPseudoRef { .. } | WorktreePrivate => return None,
150 })
151 }
152 None => Some(name),
153 }
154}