1mod window;
4
5use crate::crc32::Crc32;
6use crate::gzip::{SourceCursor, parse_member_header};
7use crate::index::{Checkpoint, CheckpointKind, DeflateIndex, IndexError, IndexKind, WINDOW_SIZE};
8use crate::inflate::RawInflater;
9use crate::zlib::{self, Adler32};
10use crate::{DecodeError, ReadAt};
11use libz_rs_sys as z;
12use std::error::Error;
13use std::fmt::{self, Display, Formatter};
14use std::io::{self, Read, Seek, SeekFrom};
15use std::sync::Arc;
16use window::{DEFAULT_BUDGET, WindowCache};
17
18const INPUT_PAGE: usize = 128 * 1024;
19const OUTPUT_STEP: usize = 128 * 1024;
20
21#[derive(Clone, Debug)]
23#[non_exhaustive]
24pub enum IndexedReaderError {
25 Index(IndexError),
27 Decode(DecodeError),
29 Io(Arc<io::Error>),
31}
32
33impl Display for IndexedReaderError {
34 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
35 match self {
36 Self::Index(error) => write!(formatter, "invalid DEFLATE index: {error}"),
37 Self::Decode(error) => {
38 write!(formatter, "could not initialize indexed decode: {error}")
39 }
40 Self::Io(error) => write!(formatter, "could not inspect indexed source: {error}"),
41 }
42 }
43}
44
45impl Error for IndexedReaderError {
46 fn source(&self) -> Option<&(dyn Error + 'static)> {
47 match self {
48 Self::Index(error) => Some(error),
49 Self::Decode(error) => Some(error),
50 Self::Io(error) => Some(error.as_ref()),
51 }
52 }
53}
54
55impl From<IndexError> for IndexedReaderError {
56 fn from(error: IndexError) -> Self {
57 Self::Index(error)
58 }
59}
60
61impl From<DecodeError> for IndexedReaderError {
62 fn from(error: DecodeError) -> Self {
63 Self::Decode(error)
64 }
65}
66
67enum Verification {
68 Gzip { crc: Crc32, output_size: u32 },
69 Zlib(Adler32),
70}
71
72impl Verification {
73 const fn gzip() -> Self {
74 Self::Gzip {
75 crc: Crc32::new(),
76 output_size: 0,
77 }
78 }
79
80 const fn zlib() -> Self {
81 Self::Zlib(Adler32::new())
82 }
83
84 fn update(&mut self, bytes: &[u8]) {
85 match self {
86 Self::Gzip { crc, output_size } => {
87 crc.update(bytes);
88 *output_size = output_size.wrapping_add(bytes.len() as u32);
89 }
90 Self::Zlib(checksum) => checksum.update(bytes),
91 }
92 }
93}
94
95pub struct IndexedReader<R: ReadAt> {
107 source: R,
108 index: DeflateIndex,
109 source_length: u64,
110 inflater: RawInflater,
111 windows: WindowCache,
112 input: Vec<u8>,
113 input_position: usize,
114 next_input: u64,
115 decoded: Vec<u8>,
116 decoded_position: usize,
117 position: u64,
118 state: State,
119 verification: Option<Verification>,
120 window_bits: u8,
121}
122
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124enum State {
125 NeedsResume,
126 Running,
127 Ended,
128}
129
130impl<R: ReadAt> IndexedReader<R> {
131 pub fn new(source: R, index: DeflateIndex) -> Result<Self, IndexedReaderError> {
139 index.validate()?;
140 let source_length = source
141 .len()
142 .map_err(|error| IndexedReaderError::Io(Arc::new(error)))?;
143 if let Some(index_size) = index.compressed_size() {
144 if index_size != source_length {
145 return Err(IndexedReaderError::Index(IndexError::ArchiveSizeMismatch {
146 index_size,
147 archive_size: source_length,
148 }));
149 }
150 }
151 let window_bits = if index.kind() == IndexKind::Zlib {
152 let header = read_exact_from_source::<2, _>(&source, 0)
153 .map_err(|error| IndexedReaderError::Io(Arc::new(error)))?;
154 zlib::parse_header(header, 0)?
155 } else {
156 15
157 };
158 Ok(Self {
159 source,
160 index,
161 source_length,
162 inflater: RawInflater::new_with_window_bits(window_bits)?,
163 windows: WindowCache::new(DEFAULT_BUDGET),
164 input: Vec::new(),
165 input_position: 0,
166 next_input: 0,
167 decoded: Vec::new(),
168 decoded_position: 0,
169 position: 0,
170 state: State::NeedsResume,
171 verification: None,
172 window_bits,
173 })
174 }
175
176 #[must_use]
178 pub fn with_window_cache_bytes(mut self, bytes: usize) -> Self {
179 self.windows = WindowCache::new(bytes);
180 self
181 }
182
183 #[must_use]
185 pub const fn index(&self) -> &DeflateIndex {
186 &self.index
187 }
188
189 #[must_use]
191 pub const fn position(&self) -> u64 {
192 self.position
193 }
194
195 pub fn into_inner(self) -> (R, DeflateIndex) {
197 (self.source, self.index)
198 }
199
200 pub fn seek_to_line(&mut self, line: u64) -> io::Result<u64> {
212 let Some(checkpoint) = self.index.checkpoint_at_or_before_line(line) else {
213 return Err(io::Error::new(
214 io::ErrorKind::Unsupported,
215 "the index does not carry complete line metadata",
216 ));
217 };
218 let checkpoint_line = checkpoint
219 .line_offset
220 .expect("line checkpoint lookup requires complete metadata");
221 let mut remaining = line - checkpoint_line;
222 let start = checkpoint.uncompressed_offset_in_bytes;
223 self.seek(SeekFrom::Start(start))?;
224 if remaining == 0 {
225 return Ok(start);
226 }
227
228 let mut scratch = [0_u8; 64 * 1024];
229 loop {
230 let read = self.read(&mut scratch)?;
231 if read == 0 {
232 return Ok(self.position);
233 }
234 let mut consumed = 0_usize;
235 for (index, &byte) in scratch[..read].iter().enumerate() {
236 if byte != b'\n' {
237 continue;
238 }
239 remaining -= 1;
240 if remaining == 0 {
241 consumed = index + 1;
242 break;
243 }
244 }
245 if remaining == 0 {
246 let position = self.position - (read - consumed) as u64;
247 self.seek(SeekFrom::Start(position))?;
248 return Ok(position);
249 }
250 }
251 }
252
253 fn buffered(&self) -> usize {
254 self.decoded.len() - self.decoded_position
255 }
256
257 fn discard_buffers(&mut self) {
258 self.decoded.clear();
259 self.decoded_position = 0;
260 self.input.clear();
261 self.input_position = 0;
262 }
263
264 fn read_input_page(&mut self, offset: u64) -> io::Result<()> {
265 let remaining = self.source_length.saturating_sub(offset);
266 let length = usize::try_from(remaining)
267 .unwrap_or(usize::MAX)
268 .min(INPUT_PAGE);
269 self.input.clear();
270 self.input.resize(length, 0);
271 let mut filled = 0;
272 while filled < length {
273 let read = self
274 .source
275 .read_at(offset + filled as u64, &mut self.input[filled..])?;
276 if read == 0 {
277 return Err(io::Error::new(
278 io::ErrorKind::UnexpectedEof,
279 "positional source ended before its reported length",
280 ));
281 }
282 filled += read;
283 }
284 self.input_position = 0;
285 self.next_input = offset + filled as u64;
286 Ok(())
287 }
288
289 fn read_exact_at<const N: usize>(&self, offset: u64) -> io::Result<[u8; N]> {
290 let mut bytes = [0_u8; N];
291 let mut filled = 0;
292 while filled < N {
293 let read = self
294 .source
295 .read_at(offset + filled as u64, &mut bytes[filled..])?;
296 if read == 0 {
297 return Err(io::Error::new(
298 io::ErrorKind::UnexpectedEof,
299 "truncated compressed-stream framing",
300 ));
301 }
302 filled += read;
303 }
304 Ok(bytes)
305 }
306
307 fn parse_member_header(&self, offset: u64) -> io::Result<u64> {
308 let mut cursor =
309 SourceCursor::new(&self.source, INPUT_PAGE).map_err(|error| error.to_io_error())?;
310 cursor.seek(offset).map_err(|error| error.to_io_error())?;
311 let header =
312 parse_member_header(&mut cursor, offset == 0).map_err(|error| error.to_io_error())?;
313 Ok(header.deflate_start)
314 }
315
316 fn resume(&mut self) -> io::Result<()> {
317 self.discard_buffers();
318 let checkpoint = self
319 .index
320 .checkpoint_at_or_before(self.position)
321 .copied()
322 .ok_or_else(|| {
323 io::Error::new(
324 io::ErrorKind::InvalidInput,
325 "the index has no checkpoint at or before the requested offset",
326 )
327 })?;
328 let bit_offset = checkpoint.compressed_offset_in_bits;
329 let byte_offset = bit_offset / 8;
330 if byte_offset > self.source_length {
331 return Err(io::Error::new(
332 io::ErrorKind::InvalidInput,
333 "checkpoint points past the end of the source",
334 ));
335 }
336
337 self.inflater
338 .reset_with_window_bits(self.window_bits, bit_offset)
339 .map_err(|error| error.to_io_error())?;
340 let mut start = byte_offset;
341 let window = self.expanded_window(&checkpoint)?;
342 self.verification = match checkpoint.kind {
343 CheckpointKind::GzipMemberHeader => {
344 start = self.parse_member_header(byte_offset)?;
345 Some(Verification::gzip())
346 }
347 CheckpointKind::GzipMemberDeflate {
348 header_offset_in_bytes,
349 } => {
350 let parsed_start = self.parse_member_header(header_offset_in_bytes)?;
351 if parsed_start != byte_offset {
352 return Err(io::Error::new(
353 io::ErrorKind::InvalidData,
354 "member checkpoint does not match the parsed gzip header",
355 ));
356 }
357 Some(Verification::gzip())
358 }
359 CheckpointKind::ZlibHeader => {
360 let header = self.read_exact_at::<2>(byte_offset)?;
361 let parsed_window =
362 zlib::parse_header(header, byte_offset).map_err(|error| error.to_io_error())?;
363 if parsed_window != self.window_bits {
364 return Err(io::Error::new(
365 io::ErrorKind::InvalidData,
366 "zlib header window changed after reader construction",
367 ));
368 }
369 start = byte_offset + 2;
370 Some(Verification::zlib())
371 }
372 CheckpointKind::RawDeflateStart => None,
373 CheckpointKind::DeflateBlock => {
374 let remainder = (bit_offset % 8) as u8;
375 if remainder != 0 {
376 let straddled = self.read_exact_at::<1>(byte_offset)?[0];
377 self.inflater
378 .prime(8 - remainder, straddled >> remainder, bit_offset)
379 .map_err(|error| error.to_io_error())?;
380 start += 1;
381 }
382 None
383 }
384 };
385 if !window.is_empty() {
386 let allowed = 1_usize << self.window_bits;
387 let window = &window[window.len().saturating_sub(allowed)..];
388 self.inflater
389 .set_dictionary_bytes(window, bit_offset)
390 .map_err(|error| error.to_io_error())?;
391 }
392 self.read_input_page(start)?;
393 self.state = State::Running;
394
395 let mut remaining = self.position - checkpoint.uncompressed_offset_in_bytes;
396 while remaining > 0 {
397 if self.buffered() == 0 && !self.fill()? {
398 return Ok(());
399 }
400 let skipped = remaining.min(self.buffered() as u64);
401 self.decoded_position += skipped as usize;
402 remaining -= skipped;
403 }
404 Ok(())
405 }
406
407 fn expanded_window(&mut self, checkpoint: &Checkpoint) -> io::Result<Vec<u8>> {
408 let key = checkpoint.compressed_offset_in_bits;
409 if let Some(cached) = self.windows.get(key) {
410 return Ok(cached.to_vec());
411 }
412 let Some(stored) = self.index.windows().get(key) else {
413 return Ok(Vec::new());
414 };
415 let expanded = stored
416 .decompressed()
417 .map_err(io::Error::other)?
418 .into_owned();
419 if expanded.len() != WINDOW_SIZE {
420 return Err(io::Error::new(
421 io::ErrorKind::InvalidData,
422 "stored predecessor window is not exactly 32768 bytes",
423 ));
424 }
425 self.windows.insert(key, expanded.clone());
426 Ok(expanded)
427 }
428
429 fn fill(&mut self) -> io::Result<bool> {
430 loop {
431 match self.state {
432 State::Ended => return Ok(false),
433 State::NeedsResume => {
434 self.resume()?;
435 if self.buffered() > 0 {
436 return Ok(true);
437 }
438 if self.state == State::Ended {
439 return Ok(false);
440 }
441 }
442 State::Running => {}
443 }
444
445 if self.input_position >= self.input.len() {
446 if self.next_input >= self.source_length {
447 return Err(io::Error::new(
448 io::ErrorKind::UnexpectedEof,
449 "truncated DEFLATE stream",
450 ));
451 }
452 self.read_input_page(self.next_input)?;
453 }
454 if self.decoded_position == self.decoded.len() {
455 self.decoded.clear();
456 self.decoded_position = 0;
457 }
458 let produced = self.inflate_step()?;
459 if produced > 0 {
460 return Ok(true);
461 }
462 if self.state == State::Ended {
463 return Ok(false);
464 }
465 }
466 }
467
468 fn inflate_step(&mut self) -> io::Result<usize> {
469 let output_start = self.decoded.len();
470 self.decoded.reserve(OUTPUT_STEP);
471 let output_capacity = (self.decoded.capacity() - output_start).min(u32::MAX as usize);
472 let input = &self.input[self.input_position..];
473 let input_length = input.len().min(u32::MAX as usize);
474
475 self.inflater.stream.next_in = input.as_ptr();
476 self.inflater.stream.avail_in = input_length as u32;
477 self.inflater.stream.next_out = self.decoded.spare_capacity_mut().as_mut_ptr().cast();
478 self.inflater.stream.avail_out = output_capacity as u32;
479 let input_before = self.inflater.stream.avail_in;
480 let output_before = self.inflater.stream.avail_out;
481 let status = unsafe { z::inflate(&mut self.inflater.stream, z::Z_NO_FLUSH) };
484 let consumed = (input_before - self.inflater.stream.avail_in) as usize;
485 let produced = (output_before - self.inflater.stream.avail_out) as usize;
486 self.inflater.stream.next_in = std::ptr::null();
487 self.inflater.stream.avail_in = 0;
488 self.inflater.stream.next_out = std::ptr::null_mut();
489 self.inflater.stream.avail_out = 0;
490 self.input_position += consumed;
491 unsafe { self.decoded.set_len(output_start + produced) };
494 if let Some(verification) = self.verification.as_mut() {
495 verification.update(&self.decoded[output_start..]);
496 }
497
498 match status {
499 z::Z_OK if consumed != 0 || produced != 0 => Ok(produced),
500 z::Z_BUF_ERROR if consumed != 0 || produced != 0 => Ok(produced),
501 z::Z_OK | z::Z_BUF_ERROR if self.next_input >= self.source_length => Err(
502 io::Error::new(io::ErrorKind::UnexpectedEof, "truncated DEFLATE stream"),
503 ),
504 z::Z_OK | z::Z_BUF_ERROR => Err(io::Error::new(
505 io::ErrorKind::InvalidData,
506 "DEFLATE decoder made no progress",
507 )),
508 z::Z_STREAM_END => {
509 self.finish_stream()?;
510 Ok(produced)
511 }
512 z::Z_DATA_ERROR => Err(io::Error::new(
513 io::ErrorKind::InvalidData,
514 self.inflater
515 .message()
516 .unwrap_or_else(|| "invalid DEFLATE data".to_owned()),
517 )),
518 other => Err(io::Error::other(format!(
519 "unexpected DEFLATE backend status {other}"
520 ))),
521 }
522 }
523
524 fn finish_stream(&mut self) -> io::Result<()> {
525 let trailer_offset = self.next_input - (self.input.len() - self.input_position) as u64;
526 match self.index.kind() {
527 IndexKind::Gzip | IndexKind::Bgzf => self.finish_gzip_member(trailer_offset),
528 IndexKind::Zlib => self.finish_zlib_stream(trailer_offset),
529 IndexKind::RawDeflate => {
530 if trailer_offset != self.source_length {
531 return Err(io::Error::new(
532 io::ErrorKind::InvalidData,
533 "trailing data after raw DEFLATE stream",
534 ));
535 }
536 self.verification = None;
537 self.state = State::Ended;
538 Ok(())
539 }
540 }
541 }
542
543 fn finish_gzip_member(&mut self, footer_offset: u64) -> io::Result<()> {
544 let footer = self.read_exact_at::<8>(footer_offset)?;
545 if let Some(Verification::Gzip { crc, output_size }) = self.verification.take() {
546 let expected_crc = u32::from_le_bytes(footer[..4].try_into().expect("four bytes"));
547 let expected_size = u32::from_le_bytes(footer[4..].try_into().expect("four bytes"));
548 let actual_crc = crc.finish();
549 if expected_crc != actual_crc {
550 return Err(io::Error::new(
551 io::ErrorKind::InvalidData,
552 format!(
553 "gzip CRC32 mismatch: expected {expected_crc:#010x}, got {actual_crc:#010x}"
554 ),
555 ));
556 }
557 if expected_size != output_size {
558 return Err(io::Error::new(
559 io::ErrorKind::InvalidData,
560 format!(
561 "gzip ISIZE mismatch: expected {expected_size}, got {}",
562 output_size
563 ),
564 ));
565 }
566 }
567
568 let next_member = footer_offset.checked_add(8).ok_or_else(|| {
569 io::Error::new(io::ErrorKind::InvalidData, "gzip footer offset overflow")
570 })?;
571 if next_member == self.source_length {
572 self.state = State::Ended;
573 return Ok(());
574 }
575 if next_member > self.source_length {
576 return Err(io::Error::new(
577 io::ErrorKind::UnexpectedEof,
578 "truncated gzip member footer",
579 ));
580 }
581 let deflate_start = self.parse_member_header(next_member)?;
582 self.inflater
583 .reset(deflate_start.saturating_mul(8))
584 .map_err(|error| error.to_io_error())?;
585 self.verification = Some(Verification::gzip());
586 self.read_input_page(deflate_start)?;
587 self.state = State::Running;
588 Ok(())
589 }
590
591 fn finish_zlib_stream(&mut self, trailer_offset: u64) -> io::Result<()> {
592 let trailer = self.read_exact_at::<4>(trailer_offset)?;
593 if let Some(Verification::Zlib(checksum)) = self.verification.take() {
594 let expected = u32::from_be_bytes(trailer);
595 let actual = checksum.finish();
596 if expected != actual {
597 return Err(io::Error::new(
598 io::ErrorKind::InvalidData,
599 format!(
600 "zlib Adler-32 mismatch: expected {expected:#010x}, got {actual:#010x}"
601 ),
602 ));
603 }
604 }
605 let end = trailer_offset.checked_add(4).ok_or_else(|| {
606 io::Error::new(io::ErrorKind::InvalidData, "zlib trailer offset overflow")
607 })?;
608 if end != self.source_length {
609 return Err(io::Error::new(
610 io::ErrorKind::InvalidData,
611 "trailing data after zlib stream",
612 ));
613 }
614 self.state = State::Ended;
615 Ok(())
616 }
617}
618
619fn read_exact_from_source<const N: usize, R: ReadAt>(
620 source: &R,
621 offset: u64,
622) -> io::Result<[u8; N]> {
623 let mut bytes = [0_u8; N];
624 let mut filled = 0;
625 while filled < N {
626 let read = source.read_at(offset + filled as u64, &mut bytes[filled..])?;
627 if read == 0 {
628 return Err(io::Error::new(
629 io::ErrorKind::UnexpectedEof,
630 "compressed source ended before the requested framing bytes",
631 ));
632 }
633 filled += read;
634 }
635 Ok(bytes)
636}
637
638impl<R: ReadAt> Read for IndexedReader<R> {
639 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
640 if output.is_empty() {
641 return Ok(0);
642 }
643 loop {
644 if self.buffered() > 0 {
645 let count = self.buffered().min(output.len());
646 let start = self.decoded_position;
647 output[..count].copy_from_slice(&self.decoded[start..start + count]);
648 self.decoded_position += count;
649 self.position += count as u64;
650 return Ok(count);
651 }
652 if !self.fill()? {
653 return Ok(0);
654 }
655 }
656 }
657}
658
659impl<R: ReadAt> Seek for IndexedReader<R> {
660 fn seek(&mut self, target: SeekFrom) -> io::Result<u64> {
661 let position = match target {
662 SeekFrom::Start(offset) => offset,
663 SeekFrom::Current(delta) => add_offset(self.position, delta)?,
664 SeekFrom::End(delta) => add_offset(
665 self.index.uncompressed_size().ok_or_else(|| {
666 io::Error::new(
667 io::ErrorKind::Unsupported,
668 "the index does not record the decompressed size",
669 )
670 })?,
671 delta,
672 )?,
673 };
674
675 if position == self.position && self.state != State::NeedsResume {
676 return Ok(position);
677 }
678 if position > self.position && self.state == State::Running {
679 let ahead = position - self.position;
680 if ahead <= self.buffered() as u64 {
681 self.decoded_position += ahead as usize;
682 self.position = position;
683 return Ok(position);
684 }
685 }
686 self.position = position;
687 self.state = State::NeedsResume;
688 self.verification = None;
689 self.discard_buffers();
690 Ok(position)
691 }
692
693 fn stream_position(&mut self) -> io::Result<u64> {
694 Ok(self.position)
695 }
696}
697
698fn add_offset(base: u64, delta: i64) -> io::Result<u64> {
699 let result = if delta >= 0 {
700 base.checked_add(delta as u64)
701 } else {
702 base.checked_sub(delta.unsigned_abs())
703 };
704 result.ok_or_else(|| {
705 io::Error::new(
706 io::ErrorKind::InvalidInput,
707 "seek position is outside the decompressed stream",
708 )
709 })
710}