use core::array;
use core::marker::PhantomData;
use super::{
bisync,
error::ExFatError,
io::{BLOCK_SIZE, Block, BlockDevice},
};
#[derive(Debug)]
pub(crate) struct Slot {
sector_id: u32,
pub is_dirty: bool, is_used: bool, is_valid: bool, pub block: [u8; BLOCK_SIZE],
}
impl Slot {
pub const fn new() -> Self {
Self {
sector_id: 0,
is_dirty: false,
is_used: false,
is_valid: false,
block: [0u8; BLOCK_SIZE],
}
}
#[bisync]
pub async fn flush<D: BlockDevice>(&mut self, io: &mut D) -> Result<(), ExFatError<D>> {
if self.is_dirty {
assert_ne!(self.sector_id, 0);
io.write(self.sector_id, &self.block)
.await
.map_err(ExFatError::Io)?;
self.is_dirty = false;
}
Ok(())
}
}
#[derive(Debug)]
pub(crate) struct SlotCache<D: BlockDevice, const N: usize> {
pub cache: [Slot; N],
hand: usize,
_phantom: PhantomData<D>,
}
impl<D: BlockDevice, const N: usize> SlotCache<D, N> {
pub fn new() -> Self {
Self {
cache: array::from_fn(|_| Slot::new()),
hand: 0,
_phantom: PhantomData::default(),
}
}
#[bisync]
pub async fn flush(&mut self, io: &mut D) -> Result<(), ExFatError<D>> {
for slot in &mut self.cache {
slot.flush(io).await?;
}
Ok(())
}
#[bisync]
pub async fn flush_sector(&mut self, io: &mut D, sector: u32) -> Result<(), ExFatError<D>> {
for slot in &mut self.cache {
if slot.sector_id == sector {
slot.flush(io).await?;
break;
}
}
Ok(())
}
#[bisync]
pub async fn read(&mut self, sector_id: u32, io: &mut D) -> Result<&mut Slot, ExFatError<D>> {
if let Some(index) = self.cache_hit(sector_id) {
return Ok(&mut self.cache[index]);
}
let index = self.choose_victim(io).await?;
let slot = &mut self.cache[index];
io.read(sector_id, &mut slot.block)
.await
.map_err(ExFatError::Io)?;
slot.sector_id = sector_id;
slot.is_valid = true;
slot.is_used = true;
Ok(slot)
}
#[bisync]
pub async fn write(
&mut self,
io: &mut D,
sector_id: u32,
block: &Block,
) -> Result<(), ExFatError<D>> {
if let Some(index) = self.cache_hit(sector_id) {
let slot = &mut self.cache[index];
slot.block.copy_from_slice(block);
slot.is_dirty = false;
}
io.write(sector_id, block).await.map_err(ExFatError::Io)?;
Ok(())
}
fn cache_hit(&mut self, sector_id: u32) -> Option<usize> {
let (left, right) = self.cache.split_at_mut(self.hand);
for slot in right.iter_mut().chain(left) {
if slot.sector_id == sector_id {
slot.is_used = true;
return Some(self.hand);
} else {
self.hand += 1;
if self.hand == N {
self.hand = 0;
}
}
}
None
}
#[bisync]
async fn choose_victim(&mut self, io: &mut D) -> Result<usize, ExFatError<D>> {
loop {
let i = self.hand;
self.hand += 1;
if self.hand == N {
self.hand = 0;
}
let slot = &mut self.cache[i];
if !slot.is_valid {
return Ok(i);
}
if slot.is_used {
slot.is_used = false;
continue;
}
if slot.is_dirty {
Self::write_back(io, slot).await?;
}
return Ok(i);
}
}
#[bisync]
async fn write_back(io: &mut D, slot: &mut Slot) -> Result<(), ExFatError<D>> {
io.write(slot.sector_id, &slot.block)
.await
.map_err(ExFatError::Io)?;
slot.is_dirty = false;
Ok(())
}
}