##// END OF EJS Templates
rust-dirstate-map: use a more precise identity...
rust-dirstate-map: use a more precise identity This is closer to the behavior of what Python does. So far, we were checking only the inode, but this might not be good enough for the v1 case.

File last commit:

r52945:db5c202e default
r52947:ea0467ed default
Show More
parsers.rs
155 lines | 4.8 KiB | application/rls-services+xml | RustLexer
// 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.
use crate::errors::HgError;
use crate::utils::hg_path::HgPath;
use crate::{dirstate::EntryState, DirstateEntry, DirstateParents};
use byteorder::{BigEndian, WriteBytesExt};
use bytes_cast::{unaligned, BytesCast};
/// Parents are stored in the dirstate as byte hashes.
pub const PARENT_SIZE: usize = 20;
/// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
const MIN_ENTRY_SIZE: usize = 17;
type ParseResult<'a> = (
&'a DirstateParents,
Vec<(&'a HgPath, DirstateEntry)>,
Vec<(&'a HgPath, &'a HgPath)>,
);
pub fn parse_dirstate_parents(
contents: &[u8],
) -> Result<&DirstateParents, HgError> {
let contents_len = contents.len();
let (parents, _rest) =
DirstateParents::from_bytes(contents).map_err(|_| {
HgError::corrupted(format!(
"Too little data for dirstate: {contents_len} bytes.",
))
})?;
Ok(parents)
}
#[logging_timer::time("trace")]
pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
let mut copies = Vec::new();
let mut entries = Vec::new();
let parents =
parse_dirstate_entries(contents, |path, entry, copy_source| {
if let Some(source) = copy_source {
copies.push((path, source));
}
entries.push((path, *entry));
Ok(())
})?;
Ok((parents, entries, copies))
}
#[derive(BytesCast)]
#[repr(C)]
struct RawEntry {
state: u8,
mode: unaligned::I32Be,
size: unaligned::I32Be,
mtime: unaligned::I32Be,
length: unaligned::I32Be,
}
pub fn parse_dirstate_entries<'a>(
mut contents: &'a [u8],
mut each_entry: impl FnMut(
&'a HgPath,
&DirstateEntry,
Option<&'a HgPath>,
) -> Result<(), HgError>,
) -> Result<&'a DirstateParents, HgError> {
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
))
})?;
contents = rest;
while !contents.is_empty() {
let (raw_entry, rest) = RawEntry::from_bytes(contents)
.map_err(|_| HgError::corrupted(format!(
"dirstate corrupted: ran out of bytes at entry header {}, offset {}.",
entry_idx, original_len-contents.len())))?;
let entry = DirstateEntry::from_v1_data(
EntryState::try_from(raw_entry.state)?,
raw_entry.mode.get(),
raw_entry.size.get(),
raw_entry.mtime.get(),
);
let filename_len = raw_entry.length.get() as usize;
let (paths, rest) =
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))
)?;
// `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"),
);
let copy_source = iter.next().map(HgPath::new);
each_entry(path, &entry, copy_source)?;
entry_idx += 1;
contents = rest;
}
Ok(parents)
}
fn packed_filename_and_copy_source_size(
filename: &HgPath,
copy_source: Option<&HgPath>,
) -> usize {
filename.len()
+ if let Some(source) = copy_source {
b"\0".len() + source.len()
} else {
0
}
}
pub fn packed_entry_size(
filename: &HgPath,
copy_source: Option<&HgPath>,
) -> usize {
MIN_ENTRY_SIZE
+ packed_filename_and_copy_source_size(filename, copy_source)
}
pub fn pack_entry(
filename: &HgPath,
entry: &DirstateEntry,
copy_source: Option<&HgPath>,
packed: &mut Vec<u8>,
) {
let length = packed_filename_and_copy_source_size(filename, copy_source);
let (state, mode, size, mtime) = entry.v1_data();
// Unwrapping because `impl std::io::Write for Vec<u8>` never errors
packed.write_u8(state).unwrap();
packed.write_i32::<BigEndian>(mode).unwrap();
packed.write_i32::<BigEndian>(size).unwrap();
packed.write_i32::<BigEndian>(mtime).unwrap();
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());
}
}