git_zero_core/pack/
encode.rs1use crate::{
3 Error, Object, ObjectId, ObjectInfo, ObjectReader, Repository, Result, io::Write,
4 reader::DEFAULT_READ_BUFFER_SIZE, zlib,
5};
6
7pub fn write(repository: &impl Repository, output: &mut impl Write) -> Result<ObjectId> {
10 write_with_scratch(repository, &mut [0; DEFAULT_READ_BUFFER_SIZE], output)
11}
12
13pub fn write_with_scratch(
19 repository: &impl Repository,
20 scratch: &mut [u8],
21 output: &mut impl Write,
22) -> Result<ObjectId> {
23 write_encoder(repository, None, scratch, output)
24}
25
26pub fn write_selected_with_scratch(
31 repository: &impl Repository,
32 indices: &[usize],
33 scratch: &mut [u8],
34 output: &mut impl Write,
35) -> Result<ObjectId> {
36 write_encoder(repository, Some(indices), scratch, output)
37}
38
39pub(super) fn validate_order(repository: &impl Repository, indices: &[usize]) -> Result<()> {
40 let ordered = indices.windows(2).all(|pair| pair[0] < pair[1]);
41 for (position, &index) in indices.iter().enumerate() {
42 if index >= repository.object_count() {
43 return Err(Error::ObjectNotFound);
44 }
45 if !ordered && indices[..position].contains(&index) {
46 return Err(Error::InvalidObject);
47 }
48 }
49 Ok(())
50}
51
52fn write_encoder(
53 repository: &impl Repository,
54 indices: Option<&[usize]>,
55 scratch: &mut [u8],
56 output: &mut impl Write,
57) -> Result<ObjectId> {
58 let mut encoder = super::Encoder::new(repository, indices)?;
59 if scratch.is_empty() {
60 let count = indices.map_or(repository.object_count(), <[usize]>::len);
63 for position in 0..count {
64 let index = indices.map_or(position, |indices| indices[position]);
65 if repository.object(index).is_none() && repository.object_info(index)?.size != 0 {
66 return Err(Error::BufferTooSmall);
67 }
68 }
69 return drain_encoder(
70 repository,
71 &mut encoder,
72 &mut [0; DEFAULT_READ_BUFFER_SIZE],
73 output,
74 );
75 }
76 drain_encoder(repository, &mut encoder, scratch, output)
77}
78
79fn drain_encoder(
80 repository: &impl Repository,
81 encoder: &mut super::Encoder<'_>,
82 scratch: &mut [u8],
83 output: &mut impl Write,
84) -> Result<ObjectId> {
85 loop {
86 let count = encoder.read(repository, scratch)?;
87 if count == 0 {
88 return encoder.checksum().ok_or(Error::InvalidObject);
89 }
90 output.write_all(&scratch[..count])?;
91 }
92}
93
94pub(super) fn write_entry_header(info: ObjectInfo, output: &mut impl Write) -> Result<()> {
95 write_typed_header(info.kind as u8, info.size, output)
96}
97
98pub(super) fn write_typed_header(kind: u8, mut size: u64, output: &mut impl Write) -> Result<()> {
99 let mut byte = (kind << 4) | (size as u8 & 15);
100 size >>= 4;
101 loop {
102 output.write_all(&[byte | if size > 0 { 128 } else { 0 }])?;
103 if size == 0 {
104 return Ok(());
105 }
106 byte = size as u8 & 127;
107 size >>= 7;
108 }
109}
110
111pub fn write_loose(object: Object<'_>, output: &mut impl Write) -> Result<()> {
112 let mut storage = [0; 32];
113 zlib::write_parts(output, &[object.header(&mut storage)?, object.data])
114}
115
116pub fn write_loose_with_scratch(
120 repository: &impl Repository,
121 index: usize,
122 scratch: &mut [u8],
123 output: &mut impl Write,
124) -> Result<()> {
125 if let Some(object) = repository.object(index) {
126 return write_loose(object, output);
127 }
128 let mut reader = ObjectReader::new(repository, index)?;
129 let mut header = [0; 32];
130 let header = reader.info().header(&mut header)?;
131 zlib::write_reader(output, header, &mut reader, scratch)
132}