##// END OF EJS Templates
rust: Rewrite dirstate parsing usin the `bytes-cast` crate...
Simon Sapin -
r47336:f88e8ae0 default
parent child Browse files
Show More
@@ -310,7 +310,6 dependencies = [
310 310 "im-rc 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
311 311 "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
312 312 "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
313 "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
314 313 "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
315 314 "micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
316 315 "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -15,7 +15,6 derive_more = "0.99"
15 15 home = "0.5"
16 16 im-rc = "15.0.*"
17 17 lazy_static = "1.4.0"
18 memchr = "2.3.3"
19 18 rand = "0.7.3"
20 19 rand_pcg = "0.2.1"
21 20 rand_distr = "0.2.2"
@@ -7,6 +7,7
7 7
8 8 use crate::errors::HgError;
9 9 use crate::{utils::hg_path::HgPathBuf, FastHashMap};
10 use bytes_cast::{unaligned, BytesCast};
10 11 use std::collections::hash_map;
11 12 use std::convert::TryFrom;
12 13
@@ -17,7 +18,8 pub mod dirstate_tree;
17 18 pub mod parsers;
18 19 pub mod status;
19 20
20 #[derive(Debug, PartialEq, Clone)]
21 #[derive(Debug, PartialEq, Clone, BytesCast)]
22 #[repr(C)]
21 23 pub struct DirstateParents {
22 24 pub p1: [u8; 20],
23 25 pub p2: [u8; 20],
@@ -34,6 +36,16 pub struct DirstateEntry {
34 36 pub size: i32,
35 37 }
36 38
39 #[derive(BytesCast)]
40 #[repr(C)]
41 struct RawEntry {
42 state: u8,
43 mode: unaligned::I32Be,
44 size: unaligned::I32Be,
45 mtime: unaligned::I32Be,
46 length: unaligned::I32Be,
47 }
48
37 49 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
38 50 /// other parent. This allows revert to pick the right status back during a
39 51 /// merge.
@@ -386,10 +386,10 impl DirstateMap {
386 386 }
387 387
388 388 #[timed]
389 pub fn read(
389 pub fn read<'a>(
390 390 &mut self,
391 file_contents: &[u8],
392 ) -> Result<Option<DirstateParents>, DirstateError> {
391 file_contents: &'a [u8],
392 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
393 393 if file_contents.is_empty() {
394 394 return Ok(None);
395 395 }
@@ -6,13 +6,13
6 6 use crate::errors::HgError;
7 7 use crate::utils::hg_path::HgPath;
8 8 use crate::{
9 dirstate::{CopyMap, EntryState, StateMap},
9 dirstate::{CopyMap, EntryState, RawEntry, StateMap},
10 10 DirstateEntry, DirstateParents,
11 11 };
12 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
12 use byteorder::{BigEndian, WriteBytesExt};
13 use bytes_cast::BytesCast;
13 14 use micro_timer::timed;
14 15 use std::convert::{TryFrom, TryInto};
15 use std::io::Cursor;
16 16 use std::time::Duration;
17 17
18 18 /// Parents are stored in the dirstate as byte hashes.
@@ -21,65 +21,45 pub const PARENT_SIZE: usize = 20;
21 21 const MIN_ENTRY_SIZE: usize = 17;
22 22
23 23 type ParseResult<'a> = (
24 DirstateParents,
24 &'a DirstateParents,
25 25 Vec<(&'a HgPath, DirstateEntry)>,
26 26 Vec<(&'a HgPath, &'a HgPath)>,
27 27 );
28 28
29 29 #[timed]
30 pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
31 if contents.len() < PARENT_SIZE * 2 {
32 return Err(HgError::corrupted("Too little data for dirstate."));
33 }
34 let mut copies = vec![];
35 let mut entries = vec![];
30 pub fn parse_dirstate(mut contents: &[u8]) -> Result<ParseResult, HgError> {
31 let mut copies = Vec::new();
32 let mut entries = Vec::new();
36 33
37 let mut curr_pos = PARENT_SIZE * 2;
38 let parents = DirstateParents {
39 p1: contents[..PARENT_SIZE].try_into().unwrap(),
40 p2: contents[PARENT_SIZE..curr_pos].try_into().unwrap(),
41 };
34 let (parents, rest) = DirstateParents::from_bytes(contents)
35 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
36 contents = rest;
37 while !contents.is_empty() {
38 let (raw_entry, rest) = RawEntry::from_bytes(contents)
39 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
42 40
43 while curr_pos < contents.len() {
44 if curr_pos + MIN_ENTRY_SIZE > contents.len() {
45 return Err(HgError::corrupted("Overflow in dirstate."));
46 }
47 let entry_bytes = &contents[curr_pos..];
41 let entry = DirstateEntry {
42 state: EntryState::try_from(raw_entry.state)?,
43 mode: raw_entry.mode.get(),
44 mtime: raw_entry.mtime.get(),
45 size: raw_entry.size.get(),
46 };
47 let (paths, rest) =
48 u8::slice_from_bytes(rest, raw_entry.length.get() as usize)
49 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
48 50
49 let mut cursor = Cursor::new(entry_bytes);
50 // Unwraping errors from `byteorder` as we’ve already checked
51 // `MIN_ENTRY_SIZE` so the input should never be too short.
52 let state = EntryState::try_from(cursor.read_u8().unwrap())?;
53 let mode = cursor.read_i32::<BigEndian>().unwrap();
54 let size = cursor.read_i32::<BigEndian>().unwrap();
55 let mtime = cursor.read_i32::<BigEndian>().unwrap();
56 let path_len = cursor.read_i32::<BigEndian>().unwrap() as usize;
57
58 if path_len > contents.len() - curr_pos {
59 return Err(HgError::corrupted("Overflow in dirstate."));
51 // `paths` is either a single path, or two paths separated by a NULL
52 // byte
53 let mut iter = paths.splitn(2, |&byte| byte == b'\0');
54 let path = HgPath::new(
55 iter.next().expect("splitn always yields at least one item"),
56 );
57 if let Some(copy_source) = iter.next() {
58 copies.push((path, HgPath::new(copy_source)));
60 59 }
61 60
62 // Slice instead of allocating a Vec needed for `read_exact`
63 let path = &entry_bytes[MIN_ENTRY_SIZE..MIN_ENTRY_SIZE + (path_len)];
64
65 let (path, copy) = match memchr::memchr(0, path) {
66 None => (path, None),
67 Some(i) => (&path[..i], Some(&path[(i + 1)..])),
68 };
69
70 if let Some(copy_path) = copy {
71 copies.push((HgPath::new(path), HgPath::new(copy_path)));
72 };
73 entries.push((
74 HgPath::new(path),
75 DirstateEntry {
76 state,
77 mode,
78 size,
79 mtime,
80 },
81 ));
82 curr_pos = curr_pos + MIN_ENTRY_SIZE + (path_len);
61 entries.push((path, entry));
62 contents = rest;
83 63 }
84 64 Ok((parents, entries, copies))
85 65 }
@@ -374,7 +354,7 mod tests {
374 354 .collect();
375 355
376 356 assert_eq!(
377 (parents, state_map, copymap),
357 (&parents, state_map, copymap),
378 358 (new_parents, new_state_map, new_copy_map)
379 359 )
380 360 }
@@ -452,7 +432,7 mod tests {
452 432 .collect();
453 433
454 434 assert_eq!(
455 (parents, state_map, copymap),
435 (&parents, state_map, copymap),
456 436 (new_parents, new_state_map, new_copy_map)
457 437 )
458 438 }
@@ -499,7 +479,7 mod tests {
499 479
500 480 assert_eq!(
501 481 (
502 parents,
482 &parents,
503 483 [(
504 484 HgPathBuf::from_bytes(b"f1"),
505 485 DirstateEntry {
General Comments 0
You need to be logged in to leave comments. Login now