use crate::index_fsmonitor::write_fsmonitor;
use crate::index_full::{EXTENDED, FullEntry, FullIndex, INTENT_TO_ADD, SKIP_WORKTREE};
use crate::index_stat::encode_varint;
use crate::index_untracked_io::write_untracked;
const NAME_MASK: usize = 0x0fff;
fn has_extended(entry: &FullEntry) -> bool {
return entry.extended & (INTENT_TO_ADD | SKIP_WORKTREE) != 0;
}
fn write_entry(out: &mut Vec<u8>, entry: &FullEntry, earlier: Option<&mut Vec<u8>>) {
let extended: bool = has_extended(entry);
for value in [entry.stat.ctime_sec, entry.stat.ctime_nsec, entry.stat.mtime_sec, entry.stat.mtime_nsec, entry.stat.dev, entry.stat.ino, entry.mode, entry.stat.uid, entry.stat.gid, entry.stat.size] {
out.extend_from_slice(&value.to_be_bytes());
}
out.extend_from_slice(entry.id.as_slice());
let flags: u16 = entry.flags | if extended { EXTENDED } else { 0 } | entry.path.len().min(NAME_MASK) as u16;
out.extend_from_slice(&flags.to_be_bytes());
if extended {
out.extend_from_slice(&(entry.extended & (INTENT_TO_ADD | SKIP_WORKTREE)).to_be_bytes());
}
let Some(previous) = earlier else {
let fixed: usize = 40 + entry.id.len() + if extended { 4 } else { 2 };
out.extend_from_slice(entry.path.as_slice());
let padding: usize = ((fixed + entry.path.len() + 8) & !7) - (fixed + entry.path.len());
out.resize(out.len() + padding, 0);
return;
};
let common: usize = previous.iter().zip(entry.path.iter()).take_while(|(left, right)| { return left == right; }).count();
encode_varint((previous.len() - common) as u64, out);
out.extend_from_slice(&entry.path[common..]);
out.push(0);
previous.clear();
previous.extend_from_slice(entry.path.as_slice());
}
fn write_extension(out: &mut Vec<u8>, signature: &[u8; 4], content: &[u8]) {
out.extend_from_slice(signature);
out.extend_from_slice(&(content.len() as u32).to_be_bytes());
out.extend_from_slice(content);
}
pub fn write_full_index(index: &FullIndex, hash: &dyn Fn(&[u8]) -> Vec<u8>) -> Option<Vec<u8>> {
let extended: bool = index.entries.iter().any(has_extended);
let version: u32 = if index.version == 4 { 4 } else if extended { 3 } else { 2 };
let mut out: Vec<u8> = Vec::new();
out.extend_from_slice(b"DIRC");
out.extend_from_slice(&version.to_be_bytes());
out.extend_from_slice(&u32::try_from(index.entries.len()).ok()?.to_be_bytes());
let mut previous: Vec<u8> = Vec::new();
for entry in &index.entries {
write_entry(&mut out, entry, if version == 4 { Some(&mut previous) } else { None });
}
if let Some(tree) = &index.cache_tree {
write_extension(&mut out, b"TREE", tree.write().as_slice());
}
if let Some(undo) = &index.resolve_undo {
write_extension(&mut out, b"REUC", undo.write().as_slice());
}
if let Some(cache) = &index.untracked {
write_extension(&mut out, b"UNTR", write_untracked(cache).as_slice());
}
if let Some(monitor) = &index.fsmonitor {
let dirty: Vec<usize> = index.entries.iter().enumerate().filter(|(_, entry)| { return !entry.monitor_valid; }).map(|(position, _)| { return position; }).collect();
write_extension(&mut out, b"FSMN", write_fsmonitor(monitor.token.as_slice(), dirty.as_slice()).as_slice());
}
let checksum: Vec<u8> = hash(out.as_slice());
out.extend_from_slice(checksum.as_slice());
return Some(out);
}
#[cfg(test)]
#[path = "index_full_write_tests.rs"]
mod tests;