use crate::{
Error, Object, ObjectId, ObjectInfo, ObjectReader, Repository, Result, io::Write,
reader::DEFAULT_READ_BUFFER_SIZE, zlib,
};
pub fn write(repository: &impl Repository, output: &mut impl Write) -> Result<ObjectId> {
write_with_scratch(repository, &mut [0; DEFAULT_READ_BUFFER_SIZE], output)
}
pub fn write_with_scratch(
repository: &impl Repository,
scratch: &mut [u8],
output: &mut impl Write,
) -> Result<ObjectId> {
write_encoder(repository, None, scratch, output)
}
pub fn write_selected_with_scratch(
repository: &impl Repository,
indices: &[usize],
scratch: &mut [u8],
output: &mut impl Write,
) -> Result<ObjectId> {
write_encoder(repository, Some(indices), scratch, output)
}
pub(super) fn validate_order(repository: &impl Repository, indices: &[usize]) -> Result<()> {
let ordered = indices.windows(2).all(|pair| pair[0] < pair[1]);
for (position, &index) in indices.iter().enumerate() {
if index >= repository.object_count() {
return Err(Error::ObjectNotFound);
}
if !ordered && indices[..position].contains(&index) {
return Err(Error::InvalidObject);
}
}
Ok(())
}
fn write_encoder(
repository: &impl Repository,
indices: Option<&[usize]>,
scratch: &mut [u8],
output: &mut impl Write,
) -> Result<ObjectId> {
let mut encoder = super::Encoder::new(repository, indices)?;
if scratch.is_empty() {
let count = indices.map_or(repository.object_count(), <[usize]>::len);
for position in 0..count {
let index = indices.map_or(position, |indices| indices[position]);
if repository.object(index).is_none() && repository.object_info(index)?.size != 0 {
return Err(Error::BufferTooSmall);
}
}
return drain_encoder(
repository,
&mut encoder,
&mut [0; DEFAULT_READ_BUFFER_SIZE],
output,
);
}
drain_encoder(repository, &mut encoder, scratch, output)
}
fn drain_encoder(
repository: &impl Repository,
encoder: &mut super::Encoder<'_>,
scratch: &mut [u8],
output: &mut impl Write,
) -> Result<ObjectId> {
loop {
let count = encoder.read(repository, scratch)?;
if count == 0 {
return encoder.checksum().ok_or(Error::InvalidObject);
}
output.write_all(&scratch[..count])?;
}
}
pub(super) fn write_entry_header(info: ObjectInfo, output: &mut impl Write) -> Result<()> {
write_typed_header(info.kind as u8, info.size, output)
}
pub(super) fn write_typed_header(kind: u8, mut size: u64, output: &mut impl Write) -> Result<()> {
let mut byte = (kind << 4) | (size as u8 & 15);
size >>= 4;
loop {
output.write_all(&[byte | if size > 0 { 128 } else { 0 }])?;
if size == 0 {
return Ok(());
}
byte = size as u8 & 127;
size >>= 7;
}
}
pub fn write_loose(object: Object<'_>, output: &mut impl Write) -> Result<()> {
let mut storage = [0; 32];
zlib::write_parts(output, &[object.header(&mut storage)?, object.data])
}
pub fn write_loose_with_scratch(
repository: &impl Repository,
index: usize,
scratch: &mut [u8],
output: &mut impl Write,
) -> Result<()> {
if let Some(object) = repository.object(index) {
return write_loose(object, output);
}
let mut reader = ObjectReader::new(repository, index)?;
let mut header = [0; 32];
let header = reader.info().header(&mut header)?;
zlib::write_reader(output, header, &mut reader, scratch)
}