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, |
|
|
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