iso9660/file/
reader.rs

1//! File reader implementation
2
3use crate::error::{Iso9660Error, Result};
4use crate::types::{FileEntry, SECTOR_SIZE};
5use gpt_disk_io::BlockIo;
6use gpt_disk_types::Lba;
7
8/// Buffered file reader for streaming large files
9/// 
10/// Provides a seek/read interface for files that may be too large
11/// to load entirely into memory.
12pub struct FileReader<'a, B: BlockIo> {
13    block_io: &'a mut B,
14    file: FileEntry,
15    position: u64,
16}
17
18impl<'a, B: BlockIo> FileReader<'a, B> {
19    /// Create new file reader
20    pub fn new(block_io: &'a mut B, file: FileEntry) -> Self {
21        Self {
22            block_io,
23            file,
24            position: 0,
25        }
26    }
27    
28    /// Read bytes from current position
29    /// 
30    /// Returns number of bytes read (may be less than buffer size at EOF)
31    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
32        if self.position >= self.file.size {
33            return Ok(0);
34        }
35        
36        let remaining = (self.file.size - self.position) as usize;
37        let to_read = buffer.len().min(remaining);
38        
39        if to_read == 0 {
40            return Ok(0);
41        }
42        
43        // Calculate sector and offset within sector
44        let start_sector = (self.position / SECTOR_SIZE as u64) as u32;
45        let offset_in_sector = (self.position % SECTOR_SIZE as u64) as usize;
46        
47        let mut bytes_read = 0;
48        let mut sector_buf = [0u8; SECTOR_SIZE];
49        let mut current_sector = start_sector;
50        let mut current_offset = offset_in_sector;
51        
52        while bytes_read < to_read {
53            let lba = Lba(self.file.extent_lba as u64 + current_sector as u64);
54            self.block_io.read_blocks(lba, &mut sector_buf)
55                .map_err(|_| Iso9660Error::IoError)?;
56            
57            let available = SECTOR_SIZE - current_offset;
58            let chunk_size = available.min(to_read - bytes_read);
59            
60            buffer[bytes_read..bytes_read + chunk_size]
61                .copy_from_slice(&sector_buf[current_offset..current_offset + chunk_size]);
62            
63            bytes_read += chunk_size;
64            current_sector += 1;
65            current_offset = 0; // After first sector, start from beginning
66        }
67        
68        self.position += bytes_read as u64;
69        Ok(bytes_read)
70    }
71    
72    /// Seek to absolute position
73    pub fn seek(&mut self, pos: u64) {
74        self.position = pos.min(self.file.size);
75    }
76    
77    /// Seek relative to current position
78    pub fn seek_relative(&mut self, offset: i64) {
79        let new_pos = if offset < 0 {
80            self.position.saturating_sub((-offset) as u64)
81        } else {
82            self.position.saturating_add(offset as u64)
83        };
84        self.position = new_pos.min(self.file.size);
85    }
86    
87    /// Get current position
88    pub fn position(&self) -> u64 {
89        self.position
90    }
91    
92    /// Get file size
93    pub fn size(&self) -> u64 {
94        self.file.size
95    }
96    
97    /// Check if at end of file
98    pub fn is_eof(&self) -> bool {
99        self.position >= self.file.size
100    }
101    
102    /// Get remaining bytes
103    pub fn remaining(&self) -> u64 {
104        self.file.size.saturating_sub(self.position)
105    }
106}