#![cfg_attr(
feature = "document-features",
cfg_attr(doc, doc = ::document_features::document_features!())
)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![deny(unsafe_code, missing_docs, rust_2018_idioms)]
use std::{ops::Range, path::PathBuf};
use filetime::FileTime;
pub mod file;
pub mod extension;
pub mod entry;
mod access;
pub mod decode;
pub mod verify;
pub mod write;
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub enum Version {
V2 = 2,
V3 = 3,
V4 = 4,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Entry {
pub stat: entry::Stat,
pub id: git_hash::ObjectId,
pub flags: entry::Flags,
pub mode: entry::Mode,
path: Range<usize>,
}
pub struct File {
pub state: State,
pub path: PathBuf,
pub checksum: git_hash::ObjectId,
}
pub type PathStorage = Vec<u8>;
pub type PathStorageRef = [u8];
#[derive(Clone)]
pub struct State {
#[allow(dead_code)]
timestamp: FileTime,
version: Version,
entries: Vec<Entry>,
path_backing: PathStorage,
#[allow(dead_code)]
is_sparse: bool,
tree: Option<extension::Tree>,
link: Option<extension::Link>,
resolve_undo: Option<extension::resolve_undo::Paths>,
untracked: Option<extension::UntrackedCache>,
fs_monitor: Option<extension::FsMonitor>,
}
pub(crate) mod util {
use std::convert::TryInto;
#[inline]
pub fn var_int(data: &[u8]) -> Option<(u64, &[u8])> {
let (num, consumed) = git_features::decode::leb64_from_read(data).ok()?;
let data = &data[consumed..];
(num, data).into()
}
#[inline]
pub fn read_u32(data: &[u8]) -> Option<(u32, &[u8])> {
split_at_pos(data, 4).map(|(num, data)| (u32::from_be_bytes(num.try_into().unwrap()), data))
}
#[inline]
pub fn read_u64(data: &[u8]) -> Option<(u64, &[u8])> {
split_at_pos(data, 8).map(|(num, data)| (u64::from_be_bytes(num.try_into().unwrap()), data))
}
#[inline]
pub fn from_be_u32(b: &[u8]) -> u32 {
u32::from_be_bytes(b.try_into().unwrap())
}
#[inline]
pub fn split_at_byte_exclusive(data: &[u8], byte: u8) -> Option<(&[u8], &[u8])> {
if data.len() < 2 {
return None;
}
data.iter().enumerate().find_map(|(idx, b)| {
(*b == byte).then(|| {
if idx == 0 {
(&[] as &[u8], &data[1..])
} else {
let (a, b) = data.split_at(idx);
(a, &b[1..])
}
})
})
}
#[inline]
pub fn split_at_pos(data: &[u8], pos: usize) -> Option<(&[u8], &[u8])> {
if data.len() < pos {
return None;
}
data.split_at(pos).into()
}
}
#[test]
fn size_of_entry() {
assert_eq!(std::mem::size_of::<crate::Entry>(), 80);
assert_eq!(std::mem::size_of::<crate::entry::Time>(), 8);
assert_eq!(std::mem::size_of::<filetime::FileTime>(), 16);
}