use core::marker::PhantomData;
use super::{
bisync,
error::ExFatError,
file::{Touched, TouchedKind, TouchedSector},
io::{BLOCK_SIZE, BlockDevice},
slot_cache::SlotCache,
};
const MIN_CLUSER_ID: u32 = 2;
const CLUSTER_LEN: u32 = 0xFFFFFFF6;
const ENTRY_SIZE: usize = size_of::<u32>();
const NUM_ENTRIES: usize = BLOCK_SIZE / ENTRY_SIZE;
#[derive(Debug)]
pub struct Fat<D: BlockDevice, const N: usize> {
pub start_of_fat_sector: Option<u32>,
cache: SlotCache<D, N>,
_phantom: PhantomData<D>,
}
impl<D: BlockDevice, const N: usize> Fat<D, N> {
pub fn new() -> Self {
Self {
start_of_fat_sector: None,
cache: SlotCache::new(),
_phantom: PhantomData::default(),
}
}
#[bisync]
pub async fn flush(&mut self, io: &mut D) -> Result<(), ExFatError<D>> {
self.cache.flush(io).await?;
Ok(())
}
#[bisync]
pub async fn flush_sector(&mut self, io: &mut D, sector: u32) -> Result<(), ExFatError<D>> {
self.cache.flush_sector(io, sector).await?;
Ok(())
}
#[bisync]
pub(crate) async fn set(
&mut self,
io: &mut D,
touched: &mut impl Touched,
cluster_id: u32,
cluster_id_to: u32,
) -> Result<(), ExFatError<D>> {
assert!(cluster_id >= MIN_CLUSER_ID);
let sector_id = self.get_sector_id(cluster_id)?;
touched.insert(TouchedSector::new(TouchedKind::Fat, sector_id));
let slot = self.cache.read(sector_id, io).await?;
let (chunks, _remainder) = slot.block.as_chunks_mut::<ENTRY_SIZE>();
let sector_offset = (cluster_id % NUM_ENTRIES as u32) as usize;
chunks[sector_offset].copy_from_slice(&cluster_id_to.to_le_bytes());
slot.is_dirty = true;
Ok(())
}
fn get_sector_id(&self, cluster_id: u32) -> Result<u32, ExFatError<D>> {
match self.start_of_fat_sector {
Some(fat_offset) => Ok(fat_offset + cluster_id / NUM_ENTRIES as u32),
None => Err(ExFatError::Unexpected(
"attemt to access fat when not initialized",
)),
}
}
#[bisync]
pub(crate) async fn next_cluster_in_fat_chain(
&mut self,
cluster_id: u32,
io: &mut D,
) -> Result<Option<u32>, ExFatError<D>> {
assert!(cluster_id >= MIN_CLUSER_ID);
let sector_id = self.get_sector_id(cluster_id)?;
let sector_offset = (cluster_id % NUM_ENTRIES as u32) as usize;
let slot = self.cache.read(sector_id, io).await?;
let (chunks, _remainder) = slot.block.as_chunks::<ENTRY_SIZE>();
let next_cluster_id = u32::from_le_bytes(chunks[sector_offset]);
if (MIN_CLUSER_ID..CLUSTER_LEN).contains(&next_cluster_id) {
Ok(Some(next_cluster_id))
} else {
Ok(None)
}
}
}