parsers.rs
150 lines
| 4.7 KiB
| application/rls-services+xml
|
RustLexer
Raphaël Gomès
|
r42617 | // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | ||
// | ||||
// This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | ||||
Simon Sapin
|
r47168 | use crate::errors::HgError; | ||
Simon Sapin
|
r47894 | use crate::utils::hg_path::HgPath; | ||
Simon Sapin
|
r48882 | use crate::{dirstate::EntryState, DirstateEntry, DirstateParents}; | ||
Simon Sapin
|
r47336 | use byteorder::{BigEndian, WriteBytesExt}; | ||
Simon Sapin
|
r48830 | use bytes_cast::{unaligned, BytesCast}; | ||
Raphaël Gomès
|
r42617 | |||
/// Parents are stored in the dirstate as byte hashes. | ||||
Raphaël Gomès
|
r42993 | pub const PARENT_SIZE: usize = 20; | ||
Raphaël Gomès
|
r42617 | /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits. | ||
const MIN_ENTRY_SIZE: usize = 17; | ||||
Antoine Cezar
|
r45916 | type ParseResult<'a> = ( | ||
Simon Sapin
|
r47336 | &'a DirstateParents, | ||
Antoine Cezar
|
r45916 | Vec<(&'a HgPath, DirstateEntry)>, | ||
Vec<(&'a HgPath, &'a HgPath)>, | ||||
); | ||||
Raphaël Gomès
|
r42993 | |||
Simon Sapin
|
r47343 | pub fn parse_dirstate_parents( | ||
contents: &[u8], | ||||
) -> Result<&DirstateParents, HgError> { | ||||
let (parents, _rest) = DirstateParents::from_bytes(contents) | ||||
.map_err(|_| HgError::corrupted("Too little data for dirstate."))?; | ||||
Ok(parents) | ||||
} | ||||
Raphaël Gomès
|
r50808 | #[logging_timer::time("trace")] | ||
Simon Sapin
|
r47867 | pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> { | ||
Simon Sapin
|
r47336 | let mut copies = Vec::new(); | ||
let mut entries = Vec::new(); | ||||
Simon Sapin
|
r47867 | let parents = | ||
parse_dirstate_entries(contents, |path, entry, copy_source| { | ||||
if let Some(source) = copy_source { | ||||
copies.push((path, source)); | ||||
} | ||||
entries.push((path, *entry)); | ||||
Simon Sapin
|
r48126 | Ok(()) | ||
Simon Sapin
|
r47867 | })?; | ||
Ok((parents, entries, copies)) | ||||
} | ||||
Raphaël Gomès
|
r42617 | |||
Simon Sapin
|
r48830 | #[derive(BytesCast)] | ||
#[repr(C)] | ||||
Simon Sapin
|
r48834 | struct RawEntry { | ||
Simon Sapin
|
r48830 | state: u8, | ||
mode: unaligned::I32Be, | ||||
size: unaligned::I32Be, | ||||
mtime: unaligned::I32Be, | ||||
length: unaligned::I32Be, | ||||
} | ||||
Simon Sapin
|
r47867 | pub fn parse_dirstate_entries<'a>( | ||
mut contents: &'a [u8], | ||||
Simon Sapin
|
r48126 | mut each_entry: impl FnMut( | ||
&'a HgPath, | ||||
&DirstateEntry, | ||||
Option<&'a HgPath>, | ||||
) -> Result<(), HgError>, | ||||
Simon Sapin
|
r47867 | ) -> Result<&'a DirstateParents, HgError> { | ||
Arseniy Alekseyev
|
r51429 | let mut entry_idx = 0; | ||
let original_len = contents.len(); | ||||
let (parents, rest) = | ||||
DirstateParents::from_bytes(contents).map_err(|_| { | ||||
HgError::corrupted(format!( | ||||
"Too little data for dirstate: {} bytes.", | ||||
original_len | ||||
)) | ||||
})?; | ||||
Simon Sapin
|
r47336 | contents = rest; | ||
while !contents.is_empty() { | ||||
let (raw_entry, rest) = RawEntry::from_bytes(contents) | ||||
Arseniy Alekseyev
|
r51429 | .map_err(|_| HgError::corrupted(format!( | ||
"dirstate corrupted: ran out of bytes at entry header {}, offset {}.", | ||||
entry_idx, original_len-contents.len())))?; | ||||
Raphaël Gomès
|
r42617 | |||
Simon Sapin
|
r48834 | let entry = DirstateEntry::from_v1_data( | ||
EntryState::try_from(raw_entry.state)?, | ||||
raw_entry.mode.get(), | ||||
raw_entry.size.get(), | ||||
raw_entry.mtime.get(), | ||||
); | ||||
Arseniy Alekseyev
|
r51429 | let filename_len = raw_entry.length.get() as usize; | ||
Simon Sapin
|
r47336 | let (paths, rest) = | ||
Arseniy Alekseyev
|
r51429 | u8::slice_from_bytes(rest, filename_len) | ||
.map_err(|_| | ||||
HgError::corrupted(format!( | ||||
"dirstate corrupted: ran out of bytes at entry {}, offset {} (expected {} bytes).", | ||||
entry_idx, original_len-contents.len(), filename_len)) | ||||
)?; | ||||
Raphaël Gomès
|
r42617 | |||
Simon Sapin
|
r47336 | // `paths` is either a single path, or two paths separated by a NULL | ||
// byte | ||||
let mut iter = paths.splitn(2, |&byte| byte == b'\0'); | ||||
let path = HgPath::new( | ||||
iter.next().expect("splitn always yields at least one item"), | ||||
); | ||||
Simon Sapin
|
r47867 | let copy_source = iter.next().map(HgPath::new); | ||
Simon Sapin
|
r48126 | each_entry(path, &entry, copy_source)?; | ||
Raphaël Gomès
|
r42617 | |||
Arseniy Alekseyev
|
r51429 | entry_idx += 1; | ||
Simon Sapin
|
r47336 | contents = rest; | ||
Raphaël Gomès
|
r42617 | } | ||
Simon Sapin
|
r47867 | Ok(parents) | ||
Raphaël Gomès
|
r42617 | } | ||
Simon Sapin
|
r47872 | fn packed_filename_and_copy_source_size( | ||
Simon Sapin
|
r47894 | filename: &HgPath, | ||
copy_source: Option<&HgPath>, | ||||
Simon Sapin
|
r47872 | ) -> usize { | ||
filename.len() | ||||
+ if let Some(source) = copy_source { | ||||
b"\0".len() + source.len() | ||||
} else { | ||||
0 | ||||
} | ||||
} | ||||
pub fn packed_entry_size( | ||||
Simon Sapin
|
r47894 | filename: &HgPath, | ||
copy_source: Option<&HgPath>, | ||||
Simon Sapin
|
r47872 | ) -> usize { | ||
MIN_ENTRY_SIZE | ||||
+ packed_filename_and_copy_source_size(filename, copy_source) | ||||
} | ||||
pub fn pack_entry( | ||||
Simon Sapin
|
r47894 | filename: &HgPath, | ||
Simon Sapin
|
r47872 | entry: &DirstateEntry, | ||
Simon Sapin
|
r47894 | copy_source: Option<&HgPath>, | ||
Simon Sapin
|
r47872 | packed: &mut Vec<u8>, | ||
) { | ||||
let length = packed_filename_and_copy_source_size(filename, copy_source); | ||||
Simon Sapin
|
r48834 | let (state, mode, size, mtime) = entry.v1_data(); | ||
Simon Sapin
|
r47872 | |||
// Unwrapping because `impl std::io::Write for Vec<u8>` never errors | ||||
Simon Sapin
|
r48834 | packed.write_u8(state).unwrap(); | ||
packed.write_i32::<BigEndian>(mode).unwrap(); | ||||
packed.write_i32::<BigEndian>(size).unwrap(); | ||||
packed.write_i32::<BigEndian>(mtime).unwrap(); | ||||
Simon Sapin
|
r47872 | packed.write_i32::<BigEndian>(length as i32).unwrap(); | ||
packed.extend(filename.as_bytes()); | ||||
if let Some(source) = copy_source { | ||||
packed.push(b'\0'); | ||||
packed.extend(source.as_bytes()); | ||||
} | ||||
} | ||||