1use std::io::{self, Read, Seek, SeekFrom};
10
11pub enum CompactSection {
13 PreBuilt(Vec<u8>),
15
16 MappedBlocks {
23 old_blocks: Vec<u64>,
25 },
26
27 Zeros(u64),
29}
30
31impl CompactSection {
32 fn size(&self, block_size: usize) -> u64 {
34 match self {
35 CompactSection::PreBuilt(data) => data.len() as u64,
36 CompactSection::MappedBlocks { old_blocks } => {
37 old_blocks.len() as u64 * block_size as u64
38 }
39 CompactSection::Zeros(len) => *len,
40 }
41 }
42}
43
44pub struct CompactLayout {
46 pub sections: Vec<CompactSection>,
48 pub block_size: usize,
50 pub source_data_start: u64,
56 pub source_partition_offset: u64,
58}
59
60impl CompactLayout {
61 pub fn total_size(&self) -> u64 {
63 self.sections.iter().map(|s| s.size(self.block_size)).sum()
64 }
65}
66
67struct SectionSpan {
69 start: u64,
71 end: u64,
73}
74
75pub struct CompactStreamReader<R: Read + Seek> {
81 source: R,
82 layout: CompactLayout,
83 spans: Vec<SectionSpan>,
84 position: u64,
85 total_size: u64,
86 block_buf: Vec<u8>,
87 cached_block: Option<u64>,
89}
90
91impl<R: Read + Seek> CompactStreamReader<R> {
92 pub fn new(source: R, layout: CompactLayout) -> Self {
94 let total_size = layout.total_size();
95 let block_size = layout.block_size;
96
97 let mut spans = Vec::with_capacity(layout.sections.len());
99 let mut offset = 0u64;
100 for section in &layout.sections {
101 let size = section.size(block_size);
102 spans.push(SectionSpan {
103 start: offset,
104 end: offset + size,
105 });
106 offset += size;
107 }
108
109 Self {
110 source,
111 layout,
112 spans,
113 position: 0,
114 total_size,
115 block_buf: vec![0u8; block_size],
116 cached_block: None,
117 }
118 }
119
120 pub fn total_size(&self) -> u64 {
122 self.total_size
123 }
124
125 fn load_block(&mut self, block_num: u64) -> io::Result<()> {
127 if self.cached_block == Some(block_num) {
128 return Ok(());
129 }
130 let offset = self.layout.source_partition_offset
131 + self.layout.source_data_start
132 + block_num * self.layout.block_size as u64;
133 self.source.seek(SeekFrom::Start(offset))?;
134 self.source.read_exact(&mut self.block_buf)?;
135 self.cached_block = Some(block_num);
136 Ok(())
137 }
138
139 fn find_section(&self, pos: u64) -> Option<(usize, u64)> {
142 for (i, span) in self.spans.iter().enumerate() {
145 if pos < span.end {
146 return Some((i, pos - span.start));
147 }
148 }
149 None
150 }
151}
152
153impl<R: Read + Seek> Read for CompactStreamReader<R> {
154 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
155 if self.position >= self.total_size {
156 return Ok(0);
157 }
158
159 let to_read = buf.len().min((self.total_size - self.position) as usize);
160 if to_read == 0 {
161 return Ok(0);
162 }
163
164 let mut filled = 0;
165
166 while filled < to_read {
167 let current_pos = self.position + filled as u64;
168 if current_pos >= self.total_size {
169 break;
170 }
171
172 let (section_idx, offset_in_section) = match self.find_section(current_pos) {
173 Some(v) => v,
174 None => break,
175 };
176
177 let section = &self.layout.sections[section_idx];
178 let section_size = section.size(self.layout.block_size);
179 let remaining_in_section = (section_size - offset_in_section) as usize;
180
181 match section {
182 CompactSection::PreBuilt(data) => {
183 let off = offset_in_section as usize;
184 let copy_len = (to_read - filled).min(remaining_in_section);
185 buf[filled..filled + copy_len].copy_from_slice(&data[off..off + copy_len]);
186 filled += copy_len;
187 }
188
189 CompactSection::MappedBlocks { old_blocks } => {
190 let block_size = self.layout.block_size;
191 let block_idx = offset_in_section as usize / block_size;
192 let offset_in_block = offset_in_section as usize % block_size;
193
194 if block_idx >= old_blocks.len() {
195 let copy_len = (to_read - filled).min(remaining_in_section);
197 buf[filled..filled + copy_len].fill(0);
198 filled += copy_len;
199 continue;
200 }
201
202 let source_block = old_blocks[block_idx];
203 self.load_block(source_block)?;
204
205 let remaining_in_block = block_size - offset_in_block;
206 let copy_len = (to_read - filled).min(remaining_in_block);
207 buf[filled..filled + copy_len].copy_from_slice(
208 &self.block_buf[offset_in_block..offset_in_block + copy_len],
209 );
210 filled += copy_len;
211 }
212
213 CompactSection::Zeros(_) => {
214 let copy_len = (to_read - filled).min(remaining_in_section);
215 buf[filled..filled + copy_len].fill(0);
216 filled += copy_len;
217 }
218 }
219 }
220
221 self.position += filled as u64;
222 Ok(filled)
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use std::io::Cursor;
230
231 #[test]
232 fn test_prebuilt_section() {
233 let layout = CompactLayout {
234 sections: vec![CompactSection::PreBuilt(vec![0xAA; 512])],
235 block_size: 512,
236 source_data_start: 0,
237 source_partition_offset: 0,
238 };
239 let source = Cursor::new(vec![0u8; 0]);
240 let mut reader = CompactStreamReader::new(source, layout);
241 assert_eq!(reader.total_size(), 512);
242 let mut buf = vec![0u8; 512];
243 reader.read_exact(&mut buf).unwrap();
244 assert!(buf.iter().all(|&b| b == 0xAA));
245 }
246
247 #[test]
248 fn test_zeros_section() {
249 let layout = CompactLayout {
250 sections: vec![CompactSection::Zeros(1024)],
251 block_size: 512,
252 source_data_start: 0,
253 source_partition_offset: 0,
254 };
255 let source = Cursor::new(vec![0u8; 0]);
256 let mut reader = CompactStreamReader::new(source, layout);
257 assert_eq!(reader.total_size(), 1024);
258 let mut buf = vec![0xFFu8; 1024];
259 reader.read_exact(&mut buf).unwrap();
260 assert!(buf.iter().all(|&b| b == 0x00));
261 }
262
263 #[test]
264 fn test_mapped_blocks() {
265 let mut source_data = vec![0u8; 2048];
267 source_data[512..1024].fill(0xBB); source_data[1536..2048].fill(0xDD); let layout = CompactLayout {
270 sections: vec![CompactSection::MappedBlocks {
271 old_blocks: vec![3, 1],
272 }],
273 block_size: 512,
274 source_data_start: 0,
275 source_partition_offset: 0,
276 };
277 let mut reader = CompactStreamReader::new(Cursor::new(source_data), layout);
278 assert_eq!(reader.total_size(), 1024);
279 let mut buf = vec![0u8; 1024];
280 reader.read_exact(&mut buf).unwrap();
281 assert!(buf[..512].iter().all(|&b| b == 0xDD)); assert!(buf[512..].iter().all(|&b| b == 0xBB)); }
284
285 #[test]
286 fn test_cross_section_read() {
287 let layout = CompactLayout {
289 sections: vec![
290 CompactSection::PreBuilt(vec![0xFF; 4]),
291 CompactSection::Zeros(4),
292 ],
293 block_size: 512,
294 source_data_start: 0,
295 source_partition_offset: 0,
296 };
297 let mut reader = CompactStreamReader::new(Cursor::new(vec![]), layout);
298 assert_eq!(reader.total_size(), 8);
299 let mut buf = vec![0u8; 8];
300 reader.read_exact(&mut buf).unwrap();
301 assert_eq!(&buf, &[0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00]);
302 }
303
304 #[test]
305 fn test_multi_section_layout() {
306 let mut source_data = vec![0u8; 512];
308 source_data.fill(0xCC);
309 let layout = CompactLayout {
310 sections: vec![
311 CompactSection::PreBuilt(vec![0xAA; 512]),
312 CompactSection::MappedBlocks {
313 old_blocks: vec![0],
314 },
315 CompactSection::Zeros(512),
316 ],
317 block_size: 512,
318 source_data_start: 0,
319 source_partition_offset: 0,
320 };
321 let mut reader = CompactStreamReader::new(Cursor::new(source_data), layout);
322 assert_eq!(reader.total_size(), 1536);
323
324 let mut buf = vec![0u8; 1536];
325 reader.read_exact(&mut buf).unwrap();
326 assert!(buf[..512].iter().all(|&b| b == 0xAA)); assert!(buf[512..1024].iter().all(|&b| b == 0xCC)); assert!(buf[1024..].iter().all(|&b| b == 0x00)); }
330
331 #[test]
332 fn test_partial_read() {
333 let layout = CompactLayout {
335 sections: vec![CompactSection::PreBuilt(vec![0xAA; 1024])],
336 block_size: 512,
337 source_data_start: 0,
338 source_partition_offset: 0,
339 };
340 let mut reader = CompactStreamReader::new(Cursor::new(vec![]), layout);
341
342 let mut all = Vec::new();
344 let mut buf = [0u8; 100];
345 loop {
346 let n = reader.read(&mut buf).unwrap();
347 if n == 0 {
348 break;
349 }
350 all.extend_from_slice(&buf[..n]);
351 }
352 assert_eq!(all.len(), 1024);
353 assert!(all.iter().all(|&b| b == 0xAA));
354 }
355
356 #[test]
357 fn test_read_past_end() {
358 let layout = CompactLayout {
359 sections: vec![CompactSection::PreBuilt(vec![0xAA; 10])],
360 block_size: 512,
361 source_data_start: 0,
362 source_partition_offset: 0,
363 };
364 let mut reader = CompactStreamReader::new(Cursor::new(vec![]), layout);
365 let mut buf = vec![0u8; 20];
366 assert!(reader.read_exact(&mut buf).is_err());
368 }
369
370 #[test]
371 fn test_empty_layout() {
372 let layout = CompactLayout {
373 sections: vec![],
374 block_size: 512,
375 source_data_start: 0,
376 source_partition_offset: 0,
377 };
378 let mut reader = CompactStreamReader::new(Cursor::new(vec![]), layout);
379 assert_eq!(reader.total_size(), 0);
380 let mut buf = [0u8; 1];
381 assert_eq!(reader.read(&mut buf).unwrap(), 0);
382 }
383
384 #[test]
385 fn test_source_data_start_offset() {
386 let mut source_data = vec![0u8; 2048];
389 source_data[1024..1536].fill(0xEE); let layout = CompactLayout {
391 sections: vec![CompactSection::MappedBlocks {
392 old_blocks: vec![0],
393 }],
394 block_size: 512,
395 source_data_start: 1024,
396 source_partition_offset: 0,
397 };
398 let mut reader = CompactStreamReader::new(Cursor::new(source_data), layout);
399 let mut buf = vec![0u8; 512];
400 reader.read_exact(&mut buf).unwrap();
401 assert!(buf.iter().all(|&b| b == 0xEE));
402 }
403
404 #[test]
405 fn test_partition_offset() {
406 let mut source_data = vec![0u8; 1024];
409 source_data[512..1024].fill(0xDD);
410 let layout = CompactLayout {
411 sections: vec![CompactSection::MappedBlocks {
412 old_blocks: vec![0],
413 }],
414 block_size: 512,
415 source_data_start: 0,
416 source_partition_offset: 512,
417 };
418 let mut reader = CompactStreamReader::new(Cursor::new(source_data), layout);
419 let mut buf = vec![0u8; 512];
420 reader.read_exact(&mut buf).unwrap();
421 assert!(buf.iter().all(|&b| b == 0xDD));
422 }
423
424 #[test]
425 fn test_cross_block_read_in_mapped_section() {
426 let mut source_data = vec![0u8; 1024];
428 source_data[..512].fill(0xAA); source_data[512..1024].fill(0xBB); let layout = CompactLayout {
431 sections: vec![CompactSection::MappedBlocks {
432 old_blocks: vec![0, 1],
433 }],
434 block_size: 512,
435 source_data_start: 0,
436 source_partition_offset: 0,
437 };
438 let mut reader = CompactStreamReader::new(Cursor::new(source_data), layout);
439
440 let mut buf1 = vec![0u8; 256];
442 reader.read_exact(&mut buf1).unwrap();
443 assert!(buf1.iter().all(|&b| b == 0xAA));
444
445 let mut buf2 = vec![0u8; 512];
447 reader.read_exact(&mut buf2).unwrap();
448 assert!(buf2[..256].iter().all(|&b| b == 0xAA));
449 assert!(buf2[256..].iter().all(|&b| b == 0xBB));
450
451 let mut buf3 = vec![0u8; 256];
453 reader.read_exact(&mut buf3).unwrap();
454 assert!(buf3.iter().all(|&b| b == 0xBB));
455 }
456
457 #[test]
458 fn test_total_size() {
459 let layout = CompactLayout {
460 sections: vec![
461 CompactSection::PreBuilt(vec![0; 100]),
462 CompactSection::MappedBlocks {
463 old_blocks: vec![0, 1, 2],
464 },
465 CompactSection::Zeros(200),
466 ],
467 block_size: 4096,
468 source_data_start: 0,
469 source_partition_offset: 0,
470 };
471 assert_eq!(layout.total_size(), 100 + 3 * 4096 + 200);
472 }
473
474 #[test]
475 fn test_block_cache_reuse() {
476 let mut source_data = vec![0u8; 512];
478 source_data.fill(0xAB);
479 let layout = CompactLayout {
480 sections: vec![CompactSection::MappedBlocks {
481 old_blocks: vec![0, 0],
482 }],
483 block_size: 512,
484 source_data_start: 0,
485 source_partition_offset: 0,
486 };
487 let mut reader = CompactStreamReader::new(Cursor::new(source_data), layout);
488 let mut buf = vec![0u8; 1024];
489 reader.read_exact(&mut buf).unwrap();
490 assert!(buf.iter().all(|&b| b == 0xAB));
491 }
492}