File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -215,6 +215,8 @@ pub enum Object {
215215#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
216216pub struct TreeRef < ' a > {
217217 /// The directories and files contained in this tree.
218+ ///
219+ /// Beware that the sort order isn't *quite* by name, so one may bisect only with a [`tree::EntryRef`] to handle ordering correctly.
218220 #[ cfg_attr( feature = "serde" , serde( borrow) ) ]
219221 pub entries : Vec < tree:: EntryRef < ' a > > ,
220222}
@@ -231,6 +233,8 @@ pub struct TreeRefIter<'a> {
231233#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
232234pub struct Tree {
233235 /// The directories and files contained in this tree. They must be and remain sorted by [`filename`][tree::Entry::filename].
236+ ///
237+ /// Beware that the sort order isn't *quite* by name, so one may bisect only with a [`tree::Entry`] to handle ordering correctly.
234238 pub entries : Vec < tree:: Entry > ,
235239}
236240
Original file line number Diff line number Diff line change 1+ use bstr:: BStr ;
12use std:: convert:: TryFrom ;
23
34use nom:: error:: ParseError ;
@@ -17,6 +18,27 @@ impl<'a> TreeRef<'a> {
1718 decode:: tree ( data) . map ( |( _, t) | t) . map_err ( crate :: decode:: Error :: from)
1819 }
1920
21+ /// Find an entry named `name` knowing if the entry is a directory or not, using a binary search.
22+ ///
23+ /// Note that it's impossible to binary search by name alone as the sort order is special.
24+ pub fn bisect_entry ( & self , name : & BStr , is_dir : bool ) -> Option < EntryRef < ' a > > {
25+ static NULL_HASH : gix_hash:: ObjectId = gix_hash:: Kind :: shortest ( ) . null ( ) ;
26+
27+ let search = EntryRef {
28+ mode : if is_dir {
29+ tree:: EntryMode :: Tree
30+ } else {
31+ tree:: EntryMode :: Blob
32+ } ,
33+ filename : name,
34+ oid : & NULL_HASH ,
35+ } ;
36+ self . entries
37+ . binary_search_by ( |e| e. cmp ( & search) )
38+ . ok ( )
39+ . map ( |idx| self . entries [ idx] )
40+ }
41+
2042 /// Create an instance of the empty tree.
2143 ///
2244 /// It's particularly useful as static part of a program.
Original file line number Diff line number Diff line change @@ -143,6 +143,28 @@ mod entries {
143143
144144 tree. entries . sort ( ) ;
145145 assert_eq ! ( tree. entries, expected) ;
146+ let mut failures_when_searching_by_name = 0 ;
147+ for entry in expected {
148+ assert ! (
149+ tree. entries. binary_search_by( |e| e. cmp( & entry) ) . is_ok( ) ,
150+ "ordering works with binary search"
151+ ) ;
152+ failures_when_searching_by_name += usize:: from (
153+ tree. entries
154+ . binary_search_by ( |e| e. filename . cmp ( entry. filename ) )
155+ . is_err ( ) ,
156+ ) ;
157+ assert_eq ! (
158+ tree. bisect_entry( entry. filename, entry. mode. is_tree( ) )
159+ . expect( "entry is present" ) ,
160+ entry
161+ )
162+ }
163+
164+ assert_eq ! (
165+ failures_when_searching_by_name, 2 ,
166+ "it's not possible to do a binary search by name alone"
167+ ) ;
146168
147169 let mut tree: Tree = tree. into ( ) ;
148170 let expected = tree. entries . clone ( ) ;
You can’t perform that action at this time.
0 commit comments