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