1
1
use gix_hash:: ObjectId ;
2
2
pub use gix_object:: tree:: EntryMode ;
3
3
use gix_object:: { bstr:: BStr , TreeRefIter } ;
4
+ use gix_odb:: FindExt ;
4
5
5
6
use crate :: { object:: find, Id , Tree } ;
6
7
@@ -28,22 +29,70 @@ impl<'repo> Tree<'repo> {
28
29
gix_object:: TreeRef :: from_bytes ( & self . data )
29
30
}
30
31
32
+ /// Find the entry named `name` by iteration, or return `None` if it wasn't found.
33
+ pub fn find_entry ( & self , name : impl PartialEq < BStr > ) -> Option < EntryRef < ' repo , ' _ > > {
34
+ TreeRefIter :: from_bytes ( & self . data )
35
+ . filter_map ( Result :: ok)
36
+ . find ( |entry| name. eq ( entry. filename ) )
37
+ . map ( |entry| EntryRef {
38
+ inner : entry,
39
+ repo : self . repo ,
40
+ } )
41
+ }
42
+
31
43
// TODO: tests.
32
44
/// Follow a sequence of `path` components starting from this instance, and look them up one by one until the last component
33
45
/// is looked up and its tree entry is returned.
46
+ /// Use `buf` as temporary location for sub-trees to avoid allocating a temporary buffer for each lookup.
34
47
///
35
48
/// # Performance Notes
36
49
///
37
50
/// Searching tree entries is currently done in sequence, which allows to the search to be allocation free. It would be possible
38
51
/// to re-use a vector and use a binary search instead, which might be able to improve performance over all.
39
52
/// However, a benchmark should be created first to have some data and see which trade-off to choose here.
40
53
///
41
- /// # Why is this consuming?
54
+ pub fn lookup_entry < I , P > ( & self , path : I , buf : & mut Vec < u8 > ) -> Result < Option < Entry < ' repo > > , find:: existing:: Error >
55
+ where
56
+ I : IntoIterator < Item = P > ,
57
+ P : PartialEq < BStr > ,
58
+ {
59
+ let mut path = path. into_iter ( ) . peekable ( ) ;
60
+ while let Some ( component) = path. next ( ) {
61
+ match TreeRefIter :: from_bytes ( & self . data )
62
+ . filter_map ( Result :: ok)
63
+ . find ( |entry| component. eq ( entry. filename ) )
64
+ {
65
+ Some ( entry) => {
66
+ if path. peek ( ) . is_none ( ) {
67
+ return Ok ( Some ( Entry {
68
+ inner : entry. into ( ) ,
69
+ repo : self . repo ,
70
+ } ) ) ;
71
+ } else {
72
+ let obj = self . repo . objects . find ( entry. oid , buf) ?;
73
+ if !obj. kind . is_tree ( ) {
74
+ return Ok ( None ) ;
75
+ }
76
+ }
77
+ }
78
+ None => return Ok ( None ) ,
79
+ }
80
+ }
81
+ Ok ( None )
82
+ }
83
+
84
+ /// Follow a sequence of `path` components starting from this instance, and look them up one by one until the last component
85
+ /// is looked up and its tree entry is returned, while changing this instance to point to the last seen tree.
86
+ /// Note that if the lookup fails, it may be impossible to continue making lookups through this tree.
87
+ /// It's useful to have this function to be able to reuse the internal buffer of the tree.
88
+ ///
89
+ /// # Performance Notes
90
+ ///
91
+ /// Searching tree entries is currently done in sequence, which allows to the search to be allocation free. It would be possible
92
+ /// to re-use a vector and use a binary search instead, which might be able to improve performance over all.
93
+ /// However, a benchmark should be created first to have some data and see which trade-off to choose here.
42
94
///
43
- /// The borrow checker shows pathological behaviour in loops that mutate a buffer, but also want to return from it.
44
- /// Workarounds include keeping an index and doing a separate access to the memory, which seems hard to do here without
45
- /// re-parsing the entries.
46
- pub fn lookup_entry < I , P > ( mut self , path : I ) -> Result < Option < Entry < ' repo > > , find:: existing:: Error >
95
+ pub fn peel_to_entry < I , P > ( & mut self , path : I ) -> Result < Option < Entry < ' repo > > , find:: existing:: Error >
47
96
where
48
97
I : IntoIterator < Item = P > ,
49
98
P : PartialEq < BStr > ,
@@ -62,12 +111,11 @@ impl<'repo> Tree<'repo> {
62
111
} ) ) ;
63
112
} else {
64
113
let next_id = entry. oid . to_owned ( ) ;
65
- let repo = self . repo ;
66
- drop ( self ) ;
67
- self = match repo. find_object ( next_id) ?. try_into_tree ( ) {
68
- Ok ( tree) => tree,
69
- Err ( _) => return Ok ( None ) ,
70
- } ;
114
+ let obj = self . repo . objects . find ( next_id, & mut self . data ) ?;
115
+ self . id = next_id;
116
+ if !obj. kind . is_tree ( ) {
117
+ return Ok ( None ) ;
118
+ }
71
119
}
72
120
}
73
121
None => return Ok ( None ) ,
@@ -76,18 +124,40 @@ impl<'repo> Tree<'repo> {
76
124
Ok ( None )
77
125
}
78
126
79
- /// Like [`lookup_entry()`][ Self::lookup_entry()], but takes a `Path` directly via `relative_path`, a path relative to this tree.
127
+ /// Like [`Self::lookup_entry()` ], but takes a `Path` directly via `relative_path`, a path relative to this tree.
80
128
///
81
129
/// # Note
82
130
///
83
131
/// If any path component contains illformed UTF-8 and thus can't be converted to bytes on platforms which can't do so natively,
84
132
/// the returned component will be empty which makes the lookup fail.
85
133
pub fn lookup_entry_by_path (
86
- self ,
134
+ & self ,
135
+ relative_path : impl AsRef < std:: path:: Path > ,
136
+ buf : & mut Vec < u8 > ,
137
+ ) -> Result < Option < Entry < ' repo > > , find:: existing:: Error > {
138
+ use crate :: bstr:: ByteSlice ;
139
+ self . lookup_entry (
140
+ relative_path. as_ref ( ) . components ( ) . map ( |c : std:: path:: Component < ' _ > | {
141
+ gix_path:: os_str_into_bstr ( c. as_os_str ( ) )
142
+ . unwrap_or_else ( |_| "" . into ( ) )
143
+ . as_bytes ( )
144
+ } ) ,
145
+ buf,
146
+ )
147
+ }
148
+
149
+ /// Like [`Self::peel_to_entry()`], but takes a `Path` directly via `relative_path`, a path relative to this tree.
150
+ ///
151
+ /// # Note
152
+ ///
153
+ /// If any path component contains illformed UTF-8 and thus can't be converted to bytes on platforms which can't do so natively,
154
+ /// the returned component will be empty which makes the lookup fail.
155
+ pub fn peel_to_entry_by_path (
156
+ & mut self ,
87
157
relative_path : impl AsRef < std:: path:: Path > ,
88
158
) -> Result < Option < Entry < ' repo > > , find:: existing:: Error > {
89
159
use crate :: bstr:: ByteSlice ;
90
- self . lookup_entry ( relative_path. as_ref ( ) . components ( ) . map ( |c : std:: path:: Component < ' _ > | {
160
+ self . peel_to_entry ( relative_path. as_ref ( ) . components ( ) . map ( |c : std:: path:: Component < ' _ > | {
91
161
gix_path:: os_str_into_bstr ( c. as_os_str ( ) )
92
162
. unwrap_or_else ( |_| "" . into ( ) )
93
163
. as_bytes ( )
@@ -114,8 +184,8 @@ impl<'r> std::fmt::Debug for Tree<'r> {
114
184
/// An entry in a [`Tree`], similar to an entry in a directory.
115
185
#[ derive( PartialEq , Debug , Clone ) ]
116
186
pub struct Entry < ' repo > {
117
- inner : gix_object:: tree:: Entry ,
118
- repo : & ' repo crate :: Repository ,
187
+ pub ( crate ) inner : gix_object:: tree:: Entry ,
188
+ pub ( crate ) repo : & ' repo crate :: Repository ,
119
189
}
120
190
121
191
mod entry {
0 commit comments