@@ -24,7 +24,7 @@ pub mod write;
2424pub struct Editor < ' a > {
2525 /// A way to lookup trees.
2626 find : & ' a dyn crate :: FindExt ,
27- /// The kind of hashes to produce
27+ /// The kind of hashes to produce>
2828 object_hash : gix_hash:: Kind ,
2929 /// All trees we currently hold in memory. Each of these may change while adding and removing entries.
3030 /// null-object-ids mark tree-entries whose value we don't know yet, they are placeholders that will be
@@ -44,18 +44,121 @@ pub struct Editor<'a> {
4444/// create it by converting [`EntryKind`] into `EntryMode`.
4545#[ derive( Clone , Copy , PartialEq , Eq , Ord , PartialOrd , Hash ) ]
4646#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
47- pub struct EntryMode ( pub u16 ) ;
47+ pub struct EntryMode {
48+ // Represents the value read from Git, except that "040000" is represented with 0o140000 but
49+ // "40000" is represented with 0o40000.
50+ internal : u16 ,
51+ }
52+
53+ impl TryFrom < u32 > for tree:: EntryMode {
54+ type Error = u32 ;
55+ fn try_from ( mode : u32 ) -> Result < Self , Self :: Error > {
56+ Ok ( match mode {
57+ 0o40000 | 0o120000 | 0o160000 => EntryMode { internal : mode as u16 } ,
58+ blob_mode if blob_mode & 0o100000 == 0o100000 => EntryMode { internal : mode as u16 } ,
59+ _ => return Err ( mode) ,
60+ } )
61+ }
62+ }
63+
64+ impl EntryMode {
65+ /// Expose the value as u16 (lossy, unlike the internal representation that is hidden).
66+ pub const fn value ( self ) -> u16 {
67+ // Demangle the hack: In the case where the second leftmost octet is 4 (Tree), the leftmost bit is
68+ // there to represent whether the bytes representation should have 5 or 6 octets.
69+ if self . internal & IFMT == 0o140000 {
70+ 0o040000
71+ } else {
72+ self . internal
73+ }
74+ }
75+
76+ /// Return the representation as used in the git internal format, which is octal and written
77+ /// to the `backing` buffer. The respective sub-slice that was written to is returned.
78+ pub fn as_bytes < ' a > ( & self , backing : & ' a mut [ u8 ; 6 ] ) -> & ' a BStr {
79+ if self . internal == 0 {
80+ std:: slice:: from_ref ( & b'0' )
81+ } else {
82+ for ( idx, backing_octet) in backing. iter_mut ( ) . enumerate ( ) {
83+ let bit_pos = 3 /* because base 8 and 2^3 == 8*/ * ( 6 - idx - 1 ) ;
84+ let oct_mask = 0b111 << bit_pos;
85+ let digit = ( self . internal & oct_mask) >> bit_pos;
86+ * backing_octet = b'0' + digit as u8 ;
87+ }
88+ // Hack: `0o140000` represents `"040000"`, `0o40000` represents `"40000"`.
89+ if backing[ 1 ] == b'4' {
90+ if backing[ 0 ] == b'1' {
91+ backing[ 0 ] = b'0' ;
92+ & backing[ 0 ..6 ]
93+ } else {
94+ & backing[ 1 ..6 ]
95+ }
96+ } else {
97+ & backing[ 0 ..6 ]
98+ }
99+ }
100+ . into ( )
101+ }
102+
103+ /// Construct an EntryMode from bytes represented as in the git internal format
104+ /// Return the mode and the remainder of the bytes.
105+ pub ( crate ) fn extract_from_bytes ( i : & [ u8 ] ) -> Option < ( Self , & ' _ [ u8 ] ) > {
106+ let mut mode = 0 ;
107+ let mut idx = 0 ;
108+ let mut space_pos = 0 ;
109+ if i. is_empty ( ) {
110+ return None ;
111+ }
112+ // const fn, this is why we can't have nice things (like `.iter().any()`).
113+ while idx < i. len ( ) {
114+ let b = i[ idx] ;
115+ // Delimiter, return what we got
116+ if b == b' ' {
117+ space_pos = idx;
118+ break ;
119+ }
120+ // Not a pure octal input.
121+ // Performance matters here, so `!(b'0'..=b'7').contains(&b)` won't do.
122+ #[ allow( clippy:: manual_range_contains) ]
123+ if b < b'0' || b > b'7' {
124+ return None ;
125+ }
126+ // More than 6 octal digits we must have hit the delimiter or the input was malformed.
127+ if idx > 6 {
128+ return None ;
129+ }
130+ mode = ( mode << 3 ) + ( b - b'0' ) as u16 ;
131+ idx += 1 ;
132+ }
133+ // Hack: `0o140000` represents `"040000"`, `0o40000` represents `"40000"`.
134+ if mode == 0o040000 && i[ 0 ] == b'0' {
135+ mode += 0o100000 ;
136+ }
137+ Some ( ( Self { internal : mode } , & i[ ( space_pos + 1 ) ..] ) )
138+ }
139+
140+ /// Construct an EntryMode from bytes represented as in the git internal format.
141+ pub fn from_bytes ( i : & [ u8 ] ) -> Option < Self > {
142+ Self :: extract_from_bytes ( i) . map ( |( mode, _rest) | mode)
143+ }
144+ }
48145
49146impl std:: fmt:: Debug for EntryMode {
50147 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
51- write ! ( f, "EntryMode({:#o})" , self . 0 )
148+ write ! ( f, "EntryMode(0o{})" , self . as_bytes( & mut Default :: default ( ) ) )
149+ }
150+ }
151+
152+ impl std:: fmt:: Octal for EntryMode {
153+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
154+ write ! ( f, "{}" , self . as_bytes( & mut Default :: default ( ) ) )
52155 }
53156}
54157
55158/// A discretized version of ideal and valid values for entry modes.
56159///
57160/// Note that even though it can represent every valid [mode](EntryMode), it might
58- /// loose information due to that as well.
161+ /// lose information due to that as well.
59162#[ derive( Clone , Copy , PartialEq , Eq , Debug , Ord , PartialOrd , Hash ) ]
60163#[ repr( u16 ) ]
61164#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
@@ -74,7 +177,7 @@ pub enum EntryKind {
74177
75178impl From < EntryKind > for EntryMode {
76179 fn from ( value : EntryKind ) -> Self {
77- EntryMode ( value as u16 )
180+ EntryMode { internal : value as u16 }
78181 }
79182}
80183
@@ -100,22 +203,14 @@ impl EntryKind {
100203 }
101204}
102205
103- impl std:: ops:: Deref for EntryMode {
104- type Target = u16 ;
105-
106- fn deref ( & self ) -> & Self :: Target {
107- & self . 0
108- }
109- }
110-
111206const IFMT : u16 = 0o170000 ;
112207
113208impl EntryMode {
114209 /// Discretize the raw mode into an enum with well-known state while dropping unnecessary details.
115210 pub const fn kind ( & self ) -> EntryKind {
116- let etype = self . 0 & IFMT ;
211+ let etype = self . value ( ) & IFMT ;
117212 if etype == 0o100000 {
118- if self . 0 & 0o000100 == 0o000100 {
213+ if self . value ( ) & 0o000100 == 0o000100 {
119214 EntryKind :: BlobExecutable
120215 } else {
121216 EntryKind :: Blob
@@ -131,27 +226,27 @@ impl EntryMode {
131226
132227 /// Return true if this entry mode represents a Tree/directory
133228 pub const fn is_tree ( & self ) -> bool {
134- self . 0 & IFMT == EntryKind :: Tree as u16
229+ self . value ( ) & IFMT == EntryKind :: Tree as u16
135230 }
136231
137232 /// Return true if this entry mode represents the commit of a submodule.
138233 pub const fn is_commit ( & self ) -> bool {
139- self . 0 & IFMT == EntryKind :: Commit as u16
234+ self . value ( ) & IFMT == EntryKind :: Commit as u16
140235 }
141236
142237 /// Return true if this entry mode represents a symbolic link
143238 pub const fn is_link ( & self ) -> bool {
144- self . 0 & IFMT == EntryKind :: Link as u16
239+ self . value ( ) & IFMT == EntryKind :: Link as u16
145240 }
146241
147242 /// Return true if this entry mode represents anything BUT Tree/directory
148243 pub const fn is_no_tree ( & self ) -> bool {
149- self . 0 & IFMT != EntryKind :: Tree as u16
244+ self . value ( ) & IFMT != EntryKind :: Tree as u16
150245 }
151246
152247 /// Return true if the entry is any kind of blob.
153248 pub const fn is_blob ( & self ) -> bool {
154- self . 0 & IFMT == 0o100000
249+ self . value ( ) & IFMT == 0o100000
155250 }
156251
157252 /// Return true if the entry is an executable blob.
@@ -178,27 +273,6 @@ impl EntryMode {
178273 Commit => "commit" ,
179274 }
180275 }
181-
182- /// Return the representation as used in the git internal format, which is octal and written
183- /// to the `backing` buffer. The respective sub-slice that was written to is returned.
184- pub fn as_bytes < ' a > ( & self , backing : & ' a mut [ u8 ; 6 ] ) -> & ' a BStr {
185- if self . 0 == 0 {
186- std:: slice:: from_ref ( & b'0' )
187- } else {
188- let mut nb = 0 ;
189- let mut n = self . 0 ;
190- while n > 0 {
191- let remainder = ( n % 8 ) as u8 ;
192- backing[ nb] = b'0' + remainder;
193- n /= 8 ;
194- nb += 1 ;
195- }
196- let res = & mut backing[ ..nb] ;
197- res. reverse ( ) ;
198- res
199- }
200- . into ( )
201- }
202276}
203277
204278impl TreeRef < ' _ > {
0 commit comments