##// END OF EJS Templates
rust-dirstate: remove unneeded "ref"...
Yuya Nishihara -
r43062:cc424cc1 default
parent child Browse files
Show More
@@ -1,415 +1,415 b''
1 1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5
6 6 use crate::{
7 7 dirstate::{CopyMap, EntryState, StateMap},
8 8 utils::copy_into_array,
9 9 DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError,
10 10 };
11 11 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
12 12 use std::convert::{TryFrom, TryInto};
13 13 use std::io::Cursor;
14 14 use std::time::Duration;
15 15
16 16 /// Parents are stored in the dirstate as byte hashes.
17 17 pub const PARENT_SIZE: usize = 20;
18 18 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
19 19 const MIN_ENTRY_SIZE: usize = 17;
20 20
21 21 // TODO parse/pack: is mutate-on-loop better for performance?
22 22
23 23 pub fn parse_dirstate(
24 24 state_map: &mut StateMap,
25 25 copy_map: &mut CopyMap,
26 26 contents: &[u8],
27 27 ) -> Result<DirstateParents, DirstateParseError> {
28 28 if contents.len() < PARENT_SIZE * 2 {
29 29 return Err(DirstateParseError::TooLittleData);
30 30 }
31 31
32 32 let mut curr_pos = PARENT_SIZE * 2;
33 33 let parents = DirstateParents {
34 34 p1: copy_into_array(&contents[..PARENT_SIZE]),
35 35 p2: copy_into_array(&contents[PARENT_SIZE..curr_pos]),
36 36 };
37 37
38 38 while curr_pos < contents.len() {
39 39 if curr_pos + MIN_ENTRY_SIZE > contents.len() {
40 40 return Err(DirstateParseError::Overflow);
41 41 }
42 42 let entry_bytes = &contents[curr_pos..];
43 43
44 44 let mut cursor = Cursor::new(entry_bytes);
45 45 let state = EntryState::try_from(cursor.read_u8()?)?;
46 46 let mode = cursor.read_i32::<BigEndian>()?;
47 47 let size = cursor.read_i32::<BigEndian>()?;
48 48 let mtime = cursor.read_i32::<BigEndian>()?;
49 49 let path_len = cursor.read_i32::<BigEndian>()? as usize;
50 50
51 51 if path_len > contents.len() - curr_pos {
52 52 return Err(DirstateParseError::Overflow);
53 53 }
54 54
55 55 // Slice instead of allocating a Vec needed for `read_exact`
56 56 let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)];
57 57
58 58 let (path, copy) = match memchr::memchr(0, path) {
59 59 None => (path, None),
60 60 Some(i) => (&path[..i], Some(&path[(i + 1)..])),
61 61 };
62 62
63 63 if let Some(copy_path) = copy {
64 64 copy_map.insert(path.to_owned(), copy_path.to_owned());
65 65 };
66 66 state_map.insert(
67 67 path.to_owned(),
68 68 DirstateEntry {
69 69 state,
70 70 mode,
71 71 size,
72 72 mtime,
73 73 },
74 74 );
75 75 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
76 76 }
77 77
78 78 Ok(parents)
79 79 }
80 80
81 81 /// `now` is the duration in seconds since the Unix epoch
82 82 pub fn pack_dirstate(
83 83 state_map: &mut StateMap,
84 84 copy_map: &CopyMap,
85 85 parents: DirstateParents,
86 86 now: Duration,
87 87 ) -> Result<Vec<u8>, DirstatePackError> {
88 88 // TODO move away from i32 before 2038.
89 89 let now: i32 = now.as_secs().try_into().expect("time overflow");
90 90
91 91 let expected_size: usize = state_map
92 92 .iter()
93 93 .map(|(filename, _)| {
94 94 let mut length = MIN_ENTRY_SIZE + filename.len();
95 if let Some(ref copy) = copy_map.get(filename) {
95 if let Some(copy) = copy_map.get(filename) {
96 96 length += copy.len() + 1;
97 97 }
98 98 length
99 99 })
100 100 .sum();
101 101 let expected_size = expected_size + PARENT_SIZE * 2;
102 102
103 103 let mut packed = Vec::with_capacity(expected_size);
104 104 let mut new_state_map = vec![];
105 105
106 106 packed.extend(&parents.p1);
107 107 packed.extend(&parents.p2);
108 108
109 for (ref filename, entry) in state_map.iter() {
110 let mut new_filename: Vec<u8> = filename.to_vec();
109 for (filename, entry) in state_map.iter() {
110 let mut new_filename: Vec<u8> = filename.to_owned();
111 111 let mut new_mtime: i32 = entry.mtime;
112 112 if entry.state == EntryState::Normal && entry.mtime == now {
113 113 // The file was last modified "simultaneously" with the current
114 114 // write to dirstate (i.e. within the same second for file-
115 115 // systems with a granularity of 1 sec). This commonly happens
116 116 // for at least a couple of files on 'update'.
117 117 // The user could change the file without changing its size
118 118 // within the same second. Invalidate the file's mtime in
119 119 // dirstate, forcing future 'status' calls to compare the
120 120 // contents of the file if the size is the same. This prevents
121 121 // mistakenly treating such files as clean.
122 122 new_mtime = -1;
123 123 new_state_map.push((
124 filename.to_owned().to_vec(),
124 filename.to_owned(),
125 125 DirstateEntry {
126 126 mtime: new_mtime,
127 127 ..*entry
128 128 },
129 129 ));
130 130 }
131 131
132 if let Some(copy) = copy_map.get(*filename) {
132 if let Some(copy) = copy_map.get(filename) {
133 133 new_filename.push('\0' as u8);
134 134 new_filename.extend(copy);
135 135 }
136 136
137 137 packed.write_u8(entry.state.into())?;
138 138 packed.write_i32::<BigEndian>(entry.mode)?;
139 139 packed.write_i32::<BigEndian>(entry.size)?;
140 140 packed.write_i32::<BigEndian>(new_mtime)?;
141 141 packed.write_i32::<BigEndian>(new_filename.len() as i32)?;
142 142 packed.extend(new_filename)
143 143 }
144 144
145 145 if packed.len() != expected_size {
146 146 return Err(DirstatePackError::BadSize(expected_size, packed.len()));
147 147 }
148 148
149 149 state_map.extend(new_state_map);
150 150
151 151 Ok(packed)
152 152 }
153 153
154 154 #[cfg(test)]
155 155 mod tests {
156 156 use super::*;
157 157 use std::collections::HashMap;
158 158
159 159 #[test]
160 160 fn test_pack_dirstate_empty() {
161 161 let mut state_map: StateMap = HashMap::new();
162 162 let copymap = HashMap::new();
163 163 let parents = DirstateParents {
164 164 p1: *b"12345678910111213141",
165 165 p2: *b"00000000000000000000",
166 166 };
167 167 let now = Duration::new(15000000, 0);
168 168 let expected = b"1234567891011121314100000000000000000000".to_vec();
169 169
170 170 assert_eq!(
171 171 expected,
172 172 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
173 173 );
174 174
175 175 assert!(state_map.is_empty())
176 176 }
177 177 #[test]
178 178 fn test_pack_dirstate_one_entry() {
179 179 let expected_state_map: StateMap = [(
180 180 b"f1".to_vec(),
181 181 DirstateEntry {
182 182 state: EntryState::Normal,
183 183 mode: 0o644,
184 184 size: 0,
185 185 mtime: 791231220,
186 186 },
187 187 )]
188 188 .iter()
189 189 .cloned()
190 190 .collect();
191 191 let mut state_map = expected_state_map.clone();
192 192
193 193 let copymap = HashMap::new();
194 194 let parents = DirstateParents {
195 195 p1: *b"12345678910111213141",
196 196 p2: *b"00000000000000000000",
197 197 };
198 198 let now = Duration::new(15000000, 0);
199 199 let expected = [
200 200 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
201 201 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
202 202 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
203 203 41, 58, 244, 0, 0, 0, 2, 102, 49,
204 204 ]
205 205 .to_vec();
206 206
207 207 assert_eq!(
208 208 expected,
209 209 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
210 210 );
211 211
212 212 assert_eq!(expected_state_map, state_map);
213 213 }
214 214 #[test]
215 215 fn test_pack_dirstate_one_entry_with_copy() {
216 216 let expected_state_map: StateMap = [(
217 217 b"f1".to_vec(),
218 218 DirstateEntry {
219 219 state: EntryState::Normal,
220 220 mode: 0o644,
221 221 size: 0,
222 222 mtime: 791231220,
223 223 },
224 224 )]
225 225 .iter()
226 226 .cloned()
227 227 .collect();
228 228 let mut state_map = expected_state_map.clone();
229 229 let mut copymap = HashMap::new();
230 230 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
231 231 let parents = DirstateParents {
232 232 p1: *b"12345678910111213141",
233 233 p2: *b"00000000000000000000",
234 234 };
235 235 let now = Duration::new(15000000, 0);
236 236 let expected = [
237 237 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
238 238 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
239 239 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
240 240 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
241 241 109, 101,
242 242 ]
243 243 .to_vec();
244 244
245 245 assert_eq!(
246 246 expected,
247 247 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
248 248 );
249 249 assert_eq!(expected_state_map, state_map);
250 250 }
251 251
252 252 #[test]
253 253 fn test_parse_pack_one_entry_with_copy() {
254 254 let mut state_map: StateMap = [(
255 255 b"f1".to_vec(),
256 256 DirstateEntry {
257 257 state: EntryState::Normal,
258 258 mode: 0o644,
259 259 size: 0,
260 260 mtime: 791231220,
261 261 },
262 262 )]
263 263 .iter()
264 264 .cloned()
265 265 .collect();
266 266 let mut copymap = HashMap::new();
267 267 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
268 268 let parents = DirstateParents {
269 269 p1: *b"12345678910111213141",
270 270 p2: *b"00000000000000000000",
271 271 };
272 272 let now = Duration::new(15000000, 0);
273 273 let result =
274 274 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
275 275 .unwrap();
276 276
277 277 let mut new_state_map: StateMap = HashMap::new();
278 278 let mut new_copy_map: CopyMap = HashMap::new();
279 279 let new_parents = parse_dirstate(
280 280 &mut new_state_map,
281 281 &mut new_copy_map,
282 282 result.as_slice(),
283 283 )
284 284 .unwrap();
285 285 assert_eq!(
286 286 (parents, state_map, copymap),
287 287 (new_parents, new_state_map, new_copy_map)
288 288 )
289 289 }
290 290
291 291 #[test]
292 292 fn test_parse_pack_multiple_entries_with_copy() {
293 293 let mut state_map: StateMap = [
294 294 (
295 295 b"f1".to_vec(),
296 296 DirstateEntry {
297 297 state: EntryState::Normal,
298 298 mode: 0o644,
299 299 size: 0,
300 300 mtime: 791231220,
301 301 },
302 302 ),
303 303 (
304 304 b"f2".to_vec(),
305 305 DirstateEntry {
306 306 state: EntryState::Merged,
307 307 mode: 0o777,
308 308 size: 1000,
309 309 mtime: 791231220,
310 310 },
311 311 ),
312 312 (
313 313 b"f3".to_vec(),
314 314 DirstateEntry {
315 315 state: EntryState::Removed,
316 316 mode: 0o644,
317 317 size: 234553,
318 318 mtime: 791231220,
319 319 },
320 320 ),
321 321 (
322 322 b"f4\xF6".to_vec(),
323 323 DirstateEntry {
324 324 state: EntryState::Added,
325 325 mode: 0o644,
326 326 size: -1,
327 327 mtime: -1,
328 328 },
329 329 ),
330 330 ]
331 331 .iter()
332 332 .cloned()
333 333 .collect();
334 334 let mut copymap = HashMap::new();
335 335 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
336 336 copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec());
337 337 let parents = DirstateParents {
338 338 p1: *b"12345678910111213141",
339 339 p2: *b"00000000000000000000",
340 340 };
341 341 let now = Duration::new(15000000, 0);
342 342 let result =
343 343 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
344 344 .unwrap();
345 345
346 346 let mut new_state_map: StateMap = HashMap::new();
347 347 let mut new_copy_map: CopyMap = HashMap::new();
348 348 let new_parents = parse_dirstate(
349 349 &mut new_state_map,
350 350 &mut new_copy_map,
351 351 result.as_slice(),
352 352 )
353 353 .unwrap();
354 354 assert_eq!(
355 355 (parents, state_map, copymap),
356 356 (new_parents, new_state_map, new_copy_map)
357 357 )
358 358 }
359 359
360 360 #[test]
361 361 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
362 362 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
363 363 let mut state_map: StateMap = [(
364 364 b"f1".to_vec(),
365 365 DirstateEntry {
366 366 state: EntryState::Normal,
367 367 mode: 0o644,
368 368 size: 0,
369 369 mtime: 15000000,
370 370 },
371 371 )]
372 372 .iter()
373 373 .cloned()
374 374 .collect();
375 375 let mut copymap = HashMap::new();
376 376 copymap.insert(b"f1".to_vec(), b"copyname".to_vec());
377 377 let parents = DirstateParents {
378 378 p1: *b"12345678910111213141",
379 379 p2: *b"00000000000000000000",
380 380 };
381 381 let now = Duration::new(15000000, 0);
382 382 let result =
383 383 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
384 384 .unwrap();
385 385
386 386 let mut new_state_map: StateMap = HashMap::new();
387 387 let mut new_copy_map: CopyMap = HashMap::new();
388 388 let new_parents = parse_dirstate(
389 389 &mut new_state_map,
390 390 &mut new_copy_map,
391 391 result.as_slice(),
392 392 )
393 393 .unwrap();
394 394
395 395 assert_eq!(
396 396 (
397 397 parents,
398 398 [(
399 399 b"f1".to_vec(),
400 400 DirstateEntry {
401 401 state: EntryState::Normal,
402 402 mode: 0o644,
403 403 size: 0,
404 404 mtime: -1
405 405 }
406 406 )]
407 407 .iter()
408 408 .cloned()
409 409 .collect::<StateMap>(),
410 410 copymap,
411 411 ),
412 412 (new_parents, new_state_map, new_copy_map)
413 413 )
414 414 }
415 415 }
General Comments 0
You need to be logged in to leave comments. Login now