use alloc::sync::Arc;
use memoffset::offset_of;
use super::clusters::SectorRef;
use super::context::Context;
use crate::error::Error;
use crate::fat;
use crate::io::IOWrapper;
use crate::region::data::entryset::primary::{DateTime, FileDirectory};
use crate::region::data::entryset::secondary::{Secondary, StreamExtension};
use crate::region::data::entryset::{RawEntry, ENTRY_SIZE};
use crate::region::fat::Entry;
use crate::sync::Mutex;
use crate::types::ClusterID;
#[macro_export]
macro_rules! with_context {
($context: expr) => {
match () {
#[cfg(feature = "async")]
_ => $context.lock().await,
#[cfg(all(not(feature = "async")))]
_ => $context.lock().unwrap(),
}
};
}
pub(crate) use with_context;
#[derive(Clone)]
pub(crate) struct ClusterEntry<IO> {
pub io: IOWrapper<IO>,
pub context: Arc<Mutex<Context<IO>>>,
pub fat_info: fat::Info,
pub meta: SectorRef,
pub entry_index: usize, pub sector_ref: SectorRef,
pub sector_size_shift: u8,
pub last_cluster_id: ClusterID,
pub length: u64,
pub capacity: u64,
pub closed: bool,
}
#[derive(Copy, Clone)]
pub struct TouchOption {
pub access: bool,
pub modified: bool,
}
impl Default for TouchOption {
fn default() -> Self {
Self {
access: true,
modified: true,
}
}
}
#[deasync::deasync]
impl<E, IO: crate::io::IO<Error = E>> ClusterEntry<IO> {
pub fn cluster_size_shift(&self) -> u8 {
self.sector_size_shift + self.meta.sectors_per_cluster_shift
}
pub async fn next(&mut self, sector_ref: SectorRef) -> Result<SectorRef, Error<E>> {
if let Some(sector_ref) = sector_ref.next() {
return Ok(sector_ref);
}
let cluster_id = sector_ref.cluster_id;
let sector_id = self.fat_info.fat_sector_id(cluster_id).ok_or(Error::EOF)?;
let sector = self.io.read(sector_id).await?;
match self.fat_info.next_cluster_id(sector, sector_ref.cluster_id) {
Ok(Entry::Next(cluster_id)) => Ok(sector_ref.new(cluster_id, 0)),
_ => Err(Error::EOF),
}
}
pub async fn touch(&mut self, datetime: DateTime, option: TouchOption) -> Result<(), Error<E>> {
let index = self.entry_index;
let sector_id = self.meta.id();
let sector = self.io.read(sector_id).await?;
let entries: &[[RawEntry; 16]] = unsafe { core::mem::transmute(sector) };
let mut raw_entry = entries[index / 16][index % 16];
let file_directory: &mut FileDirectory = unsafe { core::mem::transmute(&mut raw_entry) };
if option.access {
file_directory.update_last_accessed_timestamp(datetime);
}
if option.modified {
file_directory.update_last_modified_timestamp(datetime);
}
let offset = index * ENTRY_SIZE;
self.io.write(sector_id, offset, &raw_entry).await
}
}
#[deasync::deasync]
impl<E, IO: crate::io::IO<Error = E>> ClusterEntry<IO> {
pub async fn last_cluster_id(&mut self) -> Result<ClusterID, Error<E>> {
if self.last_cluster_id.valid() {
return Ok(self.last_cluster_id);
}
let mut cluster_id = self.sector_ref.cluster_id;
if !cluster_id.valid() {
return Ok(cluster_id);
}
let mut sector_id = self.fat_info.fat_sector_id(cluster_id).unwrap();
let mut sector = self.io.read(sector_id).await?;
loop {
match self.fat_info.next_cluster_id(sector, cluster_id).unwrap() {
Entry::BadCluster => return Err(Error::BadCluster(cluster_id)),
Entry::Next(id) => cluster_id = id,
Entry::Last => break,
}
let id = self.fat_info.fat_sector_id(cluster_id).unwrap();
if id != sector_id {
sector_id = id;
sector = self.io.read(sector_id).await?;
}
}
self.last_cluster_id = cluster_id;
Ok(cluster_id)
}
pub async fn allocate(&mut self) -> Result<ClusterID, Error<E>> {
let last_cluster_id = self.last_cluster_id().await?;
let mut context = with_context!(self.context);
let cluster_id = context.allocation_bitmap.allocate(last_cluster_id).await?;
self.capacity += 1 << self.cluster_size_shift();
let sector_id = self.fat_info.fat_sector_id(cluster_id).unwrap();
let offset = self.fat_info.offset(cluster_id);
let bytes = u32::to_le_bytes(cluster_id.into());
self.io.write(sector_id, offset, &bytes).await?;
let index = self.entry_index + 1;
let sector_id = self.meta.id();
let offset = index * ENTRY_SIZE + offset_of!(Secondary<StreamExtension>, data_length);
let bytes = u64::to_le_bytes(self.capacity);
self.io.write(sector_id, offset, &bytes).await?;
Ok(cluster_id)
}
pub async fn update_length(&mut self, length: u64) -> Result<(), Error<E>> {
self.length = length;
let index = self.entry_index + 1;
let sector_id = self.meta.id();
let offset = index * ENTRY_SIZE
+ offset_of!(Secondary<StreamExtension>, custom_defined)
+ offset_of!(StreamExtension, valid_data_length);
let bytes = u64::to_le_bytes(self.capacity);
self.io.write(sector_id, offset, &bytes).await
}
pub async fn close(mut self) -> Result<(), Error<E>> {
with_context!(self.context).remove_entry(self.sector_ref.cluster_id);
self.closed = true;
Ok(())
}
}
impl<IO> Drop for ClusterEntry<IO> {
fn drop(&mut self) {
if self.closed {
return;
}
match () {
#[cfg(feature = "async")]
() => {
panic!("Explicit close must be called");
}
#[cfg(not(feature = "async"))]
() => {
let mut context = self.context.lock().unwrap();
context.remove_entry(self.sector_ref.cluster_id);
}
}
}
}
#[cfg(test)]
mod test {
use memoffset::offset_of;
use crate::region::data::entryset::secondary::{Secondary, StreamExtension};
#[test]
fn test_valid_data_length_offset() {
let offset = offset_of!(Secondary<StreamExtension>, custom_defined)
+ offset_of!(StreamExtension, valid_data_length);
assert_eq!(offset, 8);
}
}