1use alloc::vec::Vec;
64
65use super::fat_table::{Fat, FatType};
66use super::io::{Read, Seek, SeekFrom, Write};
67use crate::error::{Error, Result};
68
69pub const DEFAULT_CACHE_CAPACITY: usize = 16;
71
72#[derive(Debug)]
74struct CacheEntry {
75 sector: usize,
77 data: Vec<u8>,
79 dirty: bool,
81 access_count: u64,
83}
84
85#[derive(Debug)]
90pub struct FatSectorCache {
91 entries: Vec<CacheEntry>,
93 capacity: usize,
95 sector_size: usize,
97 fat_start: usize,
99 fat_size: usize,
101 fat_count: usize,
103 access_counter: u64,
105 stats: CacheStats,
107}
108
109#[derive(Debug, Default, Clone, Copy)]
111pub struct CacheStats {
112 pub hits: u64,
114 pub misses: u64,
116 pub evictions: u64,
118 pub dirty_writes: u64,
120}
121
122impl CacheStats {
123 pub fn hit_ratio(&self) -> f64 {
125 let total = self.hits + self.misses;
126 if total == 0 {
127 0.0
128 } else {
129 self.hits as f64 / total as f64
130 }
131 }
132}
133
134impl FatSectorCache {
135 pub fn new(
145 fat_start: usize,
146 fat_size: usize,
147 fat_count: usize,
148 sector_size: usize,
149 capacity: usize,
150 ) -> Self {
151 Self {
152 entries: Vec::with_capacity(capacity),
153 capacity,
154 sector_size,
155 fat_start,
156 fat_size,
157 fat_count,
158 access_counter: 0,
159 stats: CacheStats::default(),
160 }
161 }
162
163 pub fn with_default_capacity(
165 fat_start: usize,
166 fat_size: usize,
167 fat_count: usize,
168 sector_size: usize,
169 ) -> Self {
170 Self::new(
171 fat_start,
172 fat_size,
173 fat_count,
174 sector_size,
175 DEFAULT_CACHE_CAPACITY,
176 )
177 }
178
179 pub fn stats(&self) -> CacheStats {
181 self.stats
182 }
183
184 pub fn reset_stats(&mut self) {
186 self.stats = CacheStats::default();
187 }
188
189 pub fn clear<T: Read + Write + Seek>(&mut self, writer: Option<&mut T>) -> Result<()> {
191 if let Some(w) = writer {
192 self.flush(w)?;
193 }
194 self.entries.clear();
195 Ok(())
196 }
197
198 pub fn len(&self) -> usize {
200 self.entries.len()
201 }
202
203 pub fn is_empty(&self) -> bool {
205 self.entries.is_empty()
206 }
207
208 pub fn flush<T: Write + Seek>(&mut self, writer: &mut T) -> Result<()> {
210 let fat_start = self.fat_start;
211 let fat_size = self.fat_size;
212 let fat_count = self.fat_count;
213 let sector_size = self.sector_size;
214
215 for entry in &mut self.entries {
216 if entry.dirty {
217 for i in 0..fat_count {
219 let offset = fat_start + i * fat_size + entry.sector * sector_size;
220 writer.seek(SeekFrom::Start(offset as u64))?;
221 writer.write_all(&entry.data)?;
222 }
223 entry.dirty = false;
224 self.stats.dirty_writes += 1;
225 }
226 }
227 Ok(())
228 }
229
230 fn find_sector(&mut self, sector: usize) -> Option<usize> {
232 for (i, entry) in self.entries.iter().enumerate() {
233 if entry.sector == sector {
234 return Some(i);
235 }
236 }
237 None
238 }
239
240 fn get_sector<T: Read + Seek>(&mut self, reader: &mut T, sector: usize) -> Result<&[u8]> {
246 self.access_counter += 1;
247
248 if let Some(idx) = self.find_sector(sector) {
250 self.stats.hits += 1;
251 self.entries[idx].access_count = self.access_counter;
252 return Ok(&self.entries[idx].data);
253 }
254
255 self.stats.misses += 1;
256
257 if self.entries.len() >= self.capacity {
261 self.evict_lru_clean()?;
262 }
263
264 let mut data = alloc::vec![0u8; self.sector_size];
266 let offset = self.fat_start + sector * self.sector_size;
267 reader.seek(SeekFrom::Start(offset as u64))?;
268 reader.read_exact(&mut data)?;
269
270 self.entries.push(CacheEntry {
271 sector,
272 data,
273 dirty: false,
274 access_count: self.access_counter,
275 });
276
277 Ok(&self.entries.last().unwrap().data)
278 }
279
280 fn get_sector_mut<T: Read + Write + Seek>(
286 &mut self,
287 io: &mut T,
288 sector: usize,
289 ) -> Result<&mut [u8]> {
290 self.access_counter += 1;
291
292 if let Some(idx) = self.find_sector(sector) {
293 self.stats.hits += 1;
294 self.entries[idx].access_count = self.access_counter;
295 self.entries[idx].dirty = true;
296 return Ok(&mut self.entries[idx].data);
297 }
298
299 self.stats.misses += 1;
300
301 if self.entries.len() >= self.capacity {
305 self.evict_lru_flush(io)?;
306 }
307
308 let mut data = alloc::vec![0u8; self.sector_size];
309 let offset = self.fat_start + sector * self.sector_size;
310 io.seek(SeekFrom::Start(offset as u64))?;
311 io.read_exact(&mut data)?;
312
313 self.entries.push(CacheEntry {
314 sector,
315 data,
316 dirty: true,
317 access_count: self.access_counter,
318 });
319
320 let idx = self.entries.len() - 1;
321 Ok(&mut self.entries[idx].data)
322 }
323
324 fn find_lru_index(&self) -> Option<usize> {
326 let mut lru_idx = None;
327 let mut lru_count = u64::MAX;
328 for (i, entry) in self.entries.iter().enumerate() {
329 if entry.access_count < lru_count {
330 lru_count = entry.access_count;
331 lru_idx = Some(i);
332 }
333 }
334 lru_idx
335 }
336
337 fn find_lru_clean_index(&self) -> Option<usize> {
340 let mut lru_idx = None;
341 let mut lru_count = u64::MAX;
342 for (i, entry) in self.entries.iter().enumerate() {
343 if !entry.dirty && entry.access_count < lru_count {
344 lru_count = entry.access_count;
345 lru_idx = Some(i);
346 }
347 }
348 lru_idx
349 }
350
351 fn evict_lru_clean(&mut self) -> Result<()> {
356 if self.entries.is_empty() {
357 return Ok(());
358 }
359 match self.find_lru_clean_index() {
360 Some(idx) => {
361 self.entries.swap_remove(idx);
362 self.stats.evictions += 1;
363 Ok(())
364 }
365 None => {
366 let lru = self.find_lru_index().expect("non-empty above");
370 Err(Error::CacheDirtyEviction {
371 sector: self.entries[lru].sector as u32,
372 })
373 }
374 }
375 }
376
377 fn evict_lru_flush<T: Write + Seek>(&mut self, writer: &mut T) -> Result<()> {
381 let Some(idx) = self.find_lru_index() else {
382 return Ok(());
383 };
384 if self.entries[idx].dirty {
385 for copy in 0..self.fat_count {
388 let offset = self.fat_start
389 + copy * self.fat_size
390 + self.entries[idx].sector * self.sector_size;
391 writer.seek(SeekFrom::Start(offset as u64))?;
392 writer.write_all(&self.entries[idx].data)?;
393 }
394 self.entries[idx].dirty = false;
395 self.stats.dirty_writes += 1;
396 }
397 self.entries.swap_remove(idx);
398 self.stats.evictions += 1;
399 Ok(())
400 }
401
402 pub fn read_fat12_entry<T: Read + Seek>(
408 &mut self,
409 reader: &mut T,
410 cluster: usize,
411 ) -> Result<u16> {
412 let sector_size = self.sector_size;
413
414 let byte_offset = (cluster * 3) / 2;
416 let sector = byte_offset / sector_size;
417 let offset_in_sector = byte_offset % sector_size;
418
419 let bytes = if offset_in_sector + 1 < sector_size {
421 let data = self.get_sector(reader, sector)?;
423 [data[offset_in_sector], data[offset_in_sector + 1]]
424 } else {
425 let first_byte = {
427 let data = self.get_sector(reader, sector)?;
428 data[offset_in_sector]
429 };
430
431 let second_byte = {
432 let next_sector_data = self.get_sector(reader, sector + 1)?;
433 next_sector_data[0]
434 };
435 [first_byte, second_byte]
436 };
437
438 let value = if cluster.is_multiple_of(2) {
442 u16::from(bytes[0]) | (u16::from(bytes[1] & 0x0F) << 8)
443 } else {
444 (u16::from(bytes[0]) >> 4) | (u16::from(bytes[1]) << 4)
445 };
446
447 Ok(value)
448 }
449
450 pub fn write_fat12_entry<T: Read + Write + Seek>(
456 &mut self,
457 io: &mut T,
458 cluster: usize,
459 value: u16,
460 ) -> Result<()> {
461 let sector_size = self.sector_size;
462
463 let byte_offset = (cluster * 3) / 2;
464 let sector = byte_offset / sector_size;
465 let offset_in_sector = byte_offset % sector_size;
466
467 if offset_in_sector + 1 < sector_size {
468 let data = self.get_sector_mut(io, sector)?;
470
471 if cluster.is_multiple_of(2) {
472 data[offset_in_sector] = value as u8;
473 data[offset_in_sector + 1] =
474 (data[offset_in_sector + 1] & 0xF0) | ((value >> 8) as u8 & 0x0F);
475 } else {
476 data[offset_in_sector] = (data[offset_in_sector] & 0x0F) | ((value << 4) as u8);
477 data[offset_in_sector + 1] = (value >> 4) as u8;
478 }
479 } else {
480 {
482 let data = self.get_sector_mut(io, sector)?;
483 if cluster.is_multiple_of(2) {
484 data[offset_in_sector] = value as u8;
485 } else {
486 data[offset_in_sector] = (data[offset_in_sector] & 0x0F) | ((value << 4) as u8);
487 }
488 }
489
490 {
491 let data = self.get_sector_mut(io, sector + 1)?;
492 if cluster.is_multiple_of(2) {
493 data[0] = (data[0] & 0xF0) | ((value >> 8) as u8 & 0x0F);
494 } else {
495 data[0] = (value >> 4) as u8;
496 }
497 }
498 }
499
500 Ok(())
501 }
502
503 pub fn read_fat16_entry<T: Read + Seek>(
509 &mut self,
510 reader: &mut T,
511 cluster: usize,
512 ) -> Result<u16> {
513 let sector_size = self.sector_size;
514 let byte_offset = cluster * 2;
515 let sector = byte_offset / sector_size;
516 let offset_in_sector = byte_offset % sector_size;
517
518 let data = self.get_sector(reader, sector)?;
519 let value = u16::from_le_bytes([data[offset_in_sector], data[offset_in_sector + 1]]);
520
521 Ok(value)
522 }
523
524 pub fn write_fat16_entry<T: Read + Write + Seek>(
529 &mut self,
530 io: &mut T,
531 cluster: usize,
532 value: u16,
533 ) -> Result<()> {
534 let sector_size = self.sector_size;
535 let byte_offset = cluster * 2;
536 let sector = byte_offset / sector_size;
537 let offset_in_sector = byte_offset % sector_size;
538
539 let data = self.get_sector_mut(io, sector)?;
540 let bytes = value.to_le_bytes();
541 data[offset_in_sector] = bytes[0];
542 data[offset_in_sector + 1] = bytes[1];
543
544 Ok(())
545 }
546
547 pub fn read_fat32_entry<T: Read + Seek>(
553 &mut self,
554 reader: &mut T,
555 cluster: usize,
556 ) -> Result<u32> {
557 let sector_size = self.sector_size;
558 let byte_offset = cluster * 4;
559 let sector = byte_offset / sector_size;
560 let offset_in_sector = byte_offset % sector_size;
561
562 let data = self.get_sector(reader, sector)?;
563 let value = u32::from_le_bytes([
564 data[offset_in_sector],
565 data[offset_in_sector + 1],
566 data[offset_in_sector + 2],
567 data[offset_in_sector + 3],
568 ]);
569
570 Ok(value)
571 }
572
573 pub fn write_fat32_entry<T: Read + Write + Seek>(
578 &mut self,
579 io: &mut T,
580 cluster: usize,
581 value: u32,
582 ) -> Result<()> {
583 let sector_size = self.sector_size;
584 let byte_offset = cluster * 4;
585 let sector = byte_offset / sector_size;
586 let offset_in_sector = byte_offset % sector_size;
587
588 let data = self.get_sector_mut(io, sector)?;
589 let existing = u32::from_le_bytes([
590 data[offset_in_sector],
591 data[offset_in_sector + 1],
592 data[offset_in_sector + 2],
593 data[offset_in_sector + 3],
594 ]);
595 let bytes = ((existing & 0xF000_0000) | (value & 0x0FFF_FFFF)).to_le_bytes();
596 data[offset_in_sector] = bytes[0];
597 data[offset_in_sector + 1] = bytes[1];
598 data[offset_in_sector + 2] = bytes[2];
599 data[offset_in_sector + 3] = bytes[3];
600
601 Ok(())
602 }
603}
604
605pub struct CachedFat<'a> {
607 cache: &'a mut FatSectorCache,
608 fat_type: FatType,
609 max_cluster: u32,
610}
611
612impl<'a> CachedFat<'a> {
613 pub fn new(cache: &'a mut FatSectorCache, fat: &Fat) -> Self {
615 let (fat_type, max_cluster) = match fat {
616 Fat::Fat12(f) => (FatType::Fat12, f.max_cluster() as u32),
617 Fat::Fat16(f) => (FatType::Fat16, f.max_cluster() as u32),
618 Fat::Fat32(f) => (FatType::Fat32, f.max_cluster()),
619 };
620 Self {
621 cache,
622 fat_type,
623 max_cluster,
624 }
625 }
626
627 pub fn next_cluster<T: Read + Seek>(
629 &mut self,
630 reader: &mut T,
631 cluster: usize,
632 ) -> Result<Option<u32>> {
633 match self.fat_type {
634 FatType::Fat12 => {
635 let entry = self.cache.read_fat12_entry(reader, cluster)? & 0x0FFF;
636 if entry >= 0x0FF8 {
637 Ok(None) } else if entry == 0x0FF7 {
639 Err(Error::BadCluster {
640 cluster: cluster as u32,
641 })
642 } else if entry < 2 || entry as u32 > self.max_cluster {
643 Err(Error::ClusterOutOfBounds {
644 cluster: entry as u32,
645 max: self.max_cluster,
646 })
647 } else {
648 Ok(Some(entry as u32))
649 }
650 }
651 FatType::Fat16 => {
652 let entry = self.cache.read_fat16_entry(reader, cluster)?;
653 if entry >= 0xFFF8 {
654 Ok(None) } else if entry == 0xFFF7 {
656 Err(Error::BadCluster {
657 cluster: cluster as u32,
658 })
659 } else if entry < 2 || entry as u32 > self.max_cluster {
660 Err(Error::ClusterOutOfBounds {
661 cluster: entry as u32,
662 max: self.max_cluster,
663 })
664 } else {
665 Ok(Some(entry as u32))
666 }
667 }
668 FatType::Fat32 => {
669 let entry = self.cache.read_fat32_entry(reader, cluster)? & 0x0FFF_FFFF;
670 if entry >= 0x0FFF_FFF8 {
671 Ok(None) } else if entry == 0x0FFF_FFF7 {
673 Err(Error::BadCluster {
674 cluster: cluster as u32,
675 })
676 } else if entry < 2 || entry > self.max_cluster {
677 Err(Error::ClusterOutOfBounds {
678 cluster: entry,
679 max: self.max_cluster,
680 })
681 } else {
682 Ok(Some(entry))
683 }
684 }
685 }
686 }
687
688 pub fn read_chain<T: Read + Seek>(
696 &mut self,
697 reader: &mut T,
698 start_cluster: u32,
699 ) -> Result<Vec<u32>> {
700 let mut chain = Vec::new();
701 let mut current = start_cluster;
702
703 let max_iterations = self.max_cluster as usize;
707
708 loop {
709 if current < 2 || current > self.max_cluster {
710 break;
711 }
712
713 chain.push(current);
714
715 if chain.len() > max_iterations {
716 return Err(Error::ClusterLoop { cluster: current });
717 }
718
719 match self.next_cluster(reader, current as usize)? {
720 Some(next) => current = next,
721 None => break,
722 }
723 }
724
725 Ok(chain)
726 }
727
728 pub fn flush<T: Write + Seek>(&mut self, writer: &mut T) -> Result<()> {
730 self.cache.flush(writer)
731 }
732}
733
734#[cfg(test)]
735mod tests {
736 use super::*;
737
738 #[test]
739 fn test_cache_stats() {
740 let stats = CacheStats {
741 hits: 80,
742 misses: 20,
743 evictions: 5,
744 dirty_writes: 3,
745 };
746 assert!((stats.hit_ratio() - 0.8).abs() < 0.001);
747 }
748
749 #[test]
750 fn test_cache_stats_empty() {
751 let stats = CacheStats::default();
752 assert_eq!(stats.hit_ratio(), 0.0);
753 }
754}