git_zero_core/pack/
index_stream.rs1use super::{IndexEntry, index::validate_entries};
3use crate::{Error, ObjectId, Result, object::Hash};
4
5#[derive(Clone, Copy)]
6enum Phase {
7 Header,
8 Fanout,
9 Ids,
10 Crcs,
11 Offsets,
12 LargeOffsets,
13 PackChecksum,
14 Trailer,
15 Done,
16}
17
18pub struct IndexEncoder<'a> {
23 entries: &'a [IndexEntry],
24 pack_checksum: ObjectId,
25 hash: Hash,
26 phase: Phase,
27 next: usize,
28 fanout_end: usize,
29 large: u32,
30 pending: [u8; 32],
31 position: usize,
32 length: usize,
33 size: u64,
34}
35
36impl<'a> IndexEncoder<'a> {
37 pub fn new(entries: &'a [IndexEntry], pack_checksum: ObjectId) -> Result<Self> {
38 validate_entries(entries, pack_checksum)?;
39 let large = entries
40 .iter()
41 .filter(|entry| entry.offset >= 0x8000_0000)
42 .count();
43 let size = 1032_u64
44 .checked_add((entries.len() as u64) * (pack_checksum.format().id_length() as u64 + 8))
45 .and_then(|size| size.checked_add(large as u64 * 8))
46 .and_then(|size| size.checked_add(2 * pack_checksum.format().id_length() as u64))
47 .ok_or(Error::LimitExceeded)?;
48 Ok(Self {
49 entries,
50 pack_checksum,
51 hash: pack_checksum.format().hash(),
52 phase: Phase::Header,
53 next: 0,
54 fanout_end: 0,
55 large: 0,
56 pending: [0; 32],
57 position: 0,
58 length: 0,
59 size,
60 })
61 }
62
63 pub fn size(&self) -> u64 {
65 self.size
66 }
67
68 pub fn read(&mut self, output: &mut [u8]) -> Result<usize> {
70 if self.position == self.length {
71 if matches!(self.phase, Phase::Done) {
72 return Ok(0);
73 }
74 if output.is_empty() {
75 return Err(Error::BufferTooSmall);
76 }
77 self.refill();
78 }
79 if self.position == self.length {
80 return Ok(0);
81 }
82 if output.is_empty() {
83 return Err(Error::BufferTooSmall);
84 }
85 let count = output.len().min(self.length - self.position);
86 output[..count].copy_from_slice(&self.pending[self.position..self.position + count]);
87 if !matches!(self.phase, Phase::Done) {
88 self.hash.update(&output[..count]);
89 }
90 self.position += count;
91 Ok(count)
92 }
93
94 fn field(&mut self, bytes: &[u8]) {
95 self.pending[..bytes.len()].copy_from_slice(bytes);
96 self.position = 0;
97 self.length = bytes.len();
98 }
99
100 fn advance(&mut self, phase: Phase) {
101 self.phase = phase;
102 self.next = 0;
103 }
104
105 fn refill(&mut self) {
106 loop {
107 match self.phase {
108 Phase::Header => {
109 self.field(b"\xfftOc\0\0\0\x02");
110 self.advance(Phase::Fanout);
111 }
112 Phase::Fanout if self.next < 256 => {
113 while self.fanout_end < self.entries.len()
114 && self.entries[self.fanout_end].id.as_bytes()[0] as usize <= self.next
115 {
116 self.fanout_end += 1;
117 }
118 self.field(&(self.fanout_end as u32).to_be_bytes());
119 self.next += 1;
120 }
121 Phase::Fanout => {
122 self.advance(Phase::Ids);
123 continue;
124 }
125 Phase::Ids if self.next < self.entries.len() => {
126 self.field(self.entries[self.next].id.as_bytes());
127 self.next += 1;
128 }
129 Phase::Ids => {
130 self.advance(Phase::Crcs);
131 continue;
132 }
133 Phase::Crcs if self.next < self.entries.len() => {
134 self.field(&self.entries[self.next].crc32.to_be_bytes());
135 self.next += 1;
136 }
137 Phase::Crcs => {
138 self.advance(Phase::Offsets);
139 continue;
140 }
141 Phase::Offsets if self.next < self.entries.len() => {
142 let offset = self.entries[self.next].offset;
143 let value = if offset < 0x8000_0000 {
144 offset as u32
145 } else {
146 let value = 0x8000_0000 | self.large;
147 self.large += 1;
148 value
149 };
150 self.field(&value.to_be_bytes());
151 self.next += 1;
152 }
153 Phase::Offsets => {
154 self.advance(Phase::LargeOffsets);
155 continue;
156 }
157 Phase::LargeOffsets => {
158 while self.next < self.entries.len()
159 && self.entries[self.next].offset < 0x8000_0000
160 {
161 self.next += 1;
162 }
163 if self.next == self.entries.len() {
164 self.advance(Phase::PackChecksum);
165 continue;
166 }
167 self.field(&self.entries[self.next].offset.to_be_bytes());
168 self.next += 1;
169 }
170 Phase::PackChecksum => {
171 let checksum = self.pack_checksum;
172 self.field(checksum.as_bytes());
173 self.advance(Phase::Trailer);
174 }
175 Phase::Trailer => {
176 self.field(self.hash.clone().finish().as_bytes());
177 self.advance(Phase::Done);
178 }
179 Phase::Done => {
180 self.position = 0;
181 self.length = 0;
182 }
183 }
184 return;
185 }
186 }
187}