@@ -24,7 +24,7 @@ pub mod write;
24
24
pub struct Editor < ' a > {
25
25
/// A way to lookup trees.
26
26
find : & ' a dyn crate :: FindExt ,
27
- /// The kind of hashes to produce
27
+ /// The kind of hashes to produce>
28
28
object_hash : gix_hash:: Kind ,
29
29
/// All trees we currently hold in memory. Each of these may change while adding and removing entries.
30
30
/// 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> {
44
44
/// create it by converting [`EntryKind`] into `EntryMode`.
45
45
#[ derive( Clone , Copy , PartialEq , Eq , Ord , PartialOrd , Hash ) ]
46
46
#[ 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
+ }
48
145
49
146
impl std:: fmt:: Debug for EntryMode {
50
147
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 ( ) ) )
52
155
}
53
156
}
54
157
55
158
/// A discretized version of ideal and valid values for entry modes.
56
159
///
57
160
/// 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.
59
162
#[ derive( Clone , Copy , PartialEq , Eq , Debug , Ord , PartialOrd , Hash ) ]
60
163
#[ repr( u16 ) ]
61
164
#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
@@ -74,7 +177,7 @@ pub enum EntryKind {
74
177
75
178
impl From < EntryKind > for EntryMode {
76
179
fn from ( value : EntryKind ) -> Self {
77
- EntryMode ( value as u16 )
180
+ EntryMode { internal : value as u16 }
78
181
}
79
182
}
80
183
@@ -100,22 +203,14 @@ impl EntryKind {
100
203
}
101
204
}
102
205
103
- impl std:: ops:: Deref for EntryMode {
104
- type Target = u16 ;
105
-
106
- fn deref ( & self ) -> & Self :: Target {
107
- & self . 0
108
- }
109
- }
110
-
111
206
const IFMT : u16 = 0o170000 ;
112
207
113
208
impl EntryMode {
114
209
/// Discretize the raw mode into an enum with well-known state while dropping unnecessary details.
115
210
pub const fn kind ( & self ) -> EntryKind {
116
- let etype = self . 0 & IFMT ;
211
+ let etype = self . value ( ) & IFMT ;
117
212
if etype == 0o100000 {
118
- if self . 0 & 0o000100 == 0o000100 {
213
+ if self . value ( ) & 0o000100 == 0o000100 {
119
214
EntryKind :: BlobExecutable
120
215
} else {
121
216
EntryKind :: Blob
@@ -131,27 +226,27 @@ impl EntryMode {
131
226
132
227
/// Return true if this entry mode represents a Tree/directory
133
228
pub const fn is_tree ( & self ) -> bool {
134
- self . 0 & IFMT == EntryKind :: Tree as u16
229
+ self . value ( ) & IFMT == EntryKind :: Tree as u16
135
230
}
136
231
137
232
/// Return true if this entry mode represents the commit of a submodule.
138
233
pub const fn is_commit ( & self ) -> bool {
139
- self . 0 & IFMT == EntryKind :: Commit as u16
234
+ self . value ( ) & IFMT == EntryKind :: Commit as u16
140
235
}
141
236
142
237
/// Return true if this entry mode represents a symbolic link
143
238
pub const fn is_link ( & self ) -> bool {
144
- self . 0 & IFMT == EntryKind :: Link as u16
239
+ self . value ( ) & IFMT == EntryKind :: Link as u16
145
240
}
146
241
147
242
/// Return true if this entry mode represents anything BUT Tree/directory
148
243
pub const fn is_no_tree ( & self ) -> bool {
149
- self . 0 & IFMT != EntryKind :: Tree as u16
244
+ self . value ( ) & IFMT != EntryKind :: Tree as u16
150
245
}
151
246
152
247
/// Return true if the entry is any kind of blob.
153
248
pub const fn is_blob ( & self ) -> bool {
154
- self . 0 & IFMT == 0o100000
249
+ self . value ( ) & IFMT == 0o100000
155
250
}
156
251
157
252
/// Return true if the entry is an executable blob.
@@ -178,27 +273,6 @@ impl EntryMode {
178
273
Commit => "commit" ,
179
274
}
180
275
}
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
- }
202
276
}
203
277
204
278
impl TreeRef < ' _ > {
0 commit comments