use core::fmt::Debug;
use core::ops::Deref;
use super::meta::MetaFileDirectory;
use crate::error::{Error, InputError, OperationError};
use crate::file::{FileOptions, TouchOptions};
use crate::fs::SectorIndex;
use crate::io::{self, Block, Wrap};
use crate::region::data::entryset::primary::DateTime;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SeekFrom {
Start(u64),
End(i64),
Current(i64),
}
pub struct File<B: Deref<Target = [Block]>, E: Debug, IO>
where
IO: io::IO<Block<'static> = B, Error = E>,
{
pub(crate) meta: MetaFileDirectory<IO>,
pub(crate) sector_index: SectorIndex,
pub(crate) size: u64,
cursor: u64,
dirty: bool,
#[cfg(feature = "async")]
closed: bool,
}
impl<B: Deref<Target = [Block]>, E: Debug, IO> File<B, E, IO>
where
IO: io::IO<Block<'static> = B, Error = E>,
{
pub(crate) fn new(meta: MetaFileDirectory<IO>, sector_index: SectorIndex) -> Self {
let size = meta.metadata.length();
match () {
#[cfg(not(feature = "async"))]
() => Self { meta, sector_index, size, cursor: 0, dirty: false },
#[cfg(feature = "async")]
() => Self { meta, sector_index, size, cursor: 0, dirty: false, closed: false },
}
}
pub fn change_options(&mut self, f: impl Fn(&mut FileOptions)) {
f(&mut self.meta.options)
}
}
#[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)]
impl<B: Deref<Target = [Block]>, E: Debug, IO> File<B, E, IO>
where
IO: io::IO<Block<'static> = B, Error = E>,
{
pub fn size(&self) -> u64 {
self.size
}
pub async fn touch(&mut self, datetime: DateTime, opts: TouchOptions) -> Result<(), Error<E>> {
self.meta.touch(datetime, opts).await?;
self.meta.io.acquire().await.wrap().flush().await
}
pub async fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Error<E>> {
if self.cursor == self.size {
return Err(OperationError::EOF.into());
}
if buf.len() > (self.size - self.cursor) as usize {
buf = &mut buf[..(self.size - self.cursor) as usize];
}
let sector_size = self.meta.fs.sector_size() as usize;
let offset = self.cursor as usize % sector_size;
let sector_id = self.sector_index.id(&self.meta.fs);
let sector_remain = sector_size - offset;
let mut io = self.meta.io.acquire().await.wrap();
let sector = io.read(sector_id).await?;
let bytes = crate::io::flatten(&*sector);
if buf.len() <= sector_remain {
buf.copy_from_slice(&bytes[offset..offset + buf.len()]);
drop(io);
if buf.len() == sector_remain {
self.sector_index = self.meta.next(self.sector_index).await?;
}
self.cursor += buf.len() as u64;
return Ok(buf.len());
}
buf[..sector_remain].copy_from_slice(&bytes[offset..]);
drop(io);
let mut remain = &mut buf[sector_remain..];
self.sector_index = self.meta.next(self.sector_index).await?;
for _ in 0..remain.len() / sector_size {
let mut io = self.meta.io.acquire().await.wrap();
let sector = io.read(sector_id).await?;
let bytes = crate::io::flatten(&*sector);
remain[..sector_size].copy_from_slice(bytes);
drop(io);
self.sector_index = self.meta.next(self.sector_index).await?;
remain = &mut remain[sector_size..];
}
let mut io = self.meta.io.acquire().await.wrap();
let sector = io.read(sector_id).await?;
let bytes = crate::io::flatten(&*sector);
remain.copy_from_slice(&bytes[..remain.len()]);
self.cursor += buf.len() as u64;
Ok(buf.len())
}
pub async fn write(&mut self, bytes: &[u8]) -> Result<usize, Error<E>> {
if bytes.len() == 0 {
return Ok(0);
}
self.dirty = true;
let sector_size = self.meta.fs.sector_size() as usize;
let mut capacity = self.meta.metadata.capacity();
let sector_remain = (capacity - self.cursor) as usize % sector_size;
if sector_remain > 0 {
let length = core::cmp::min(bytes.len(), sector_remain);
let chunk = &bytes[..length];
trace!("Write to sector-ref {}", self.sector_index);
let sector_id = self.sector_index.id(&self.meta.fs);
let mut io = self.meta.io.acquire().await.wrap();
io.write(sector_id, self.cursor as usize % sector_size, chunk).await?;
drop(io);
self.cursor += length as u64;
self.size = core::cmp::max(self.cursor, self.size);
if length == sector_remain && self.cursor < capacity {
self.sector_index = self.meta.next(self.sector_index).await?;
}
return Ok(sector_remain);
}
if self.cursor >= capacity {
let cluster_id = self.meta.allocate(self.sector_index.cluster_id).await?;
self.sector_index = SectorIndex::new(cluster_id, 0);
capacity = self.meta.metadata.capacity();
}
trace!("Write to sector-ref {}", self.sector_index);
let sector_id = self.sector_index.id(&self.meta.fs);
let length = core::cmp::min(bytes.len(), sector_size);
let chunk = &bytes[..length];
self.meta.io.acquire().await.wrap().write(sector_id, 0, chunk).await?;
self.cursor += length as u64;
self.size = core::cmp::max(self.cursor, self.size);
if length == sector_size && self.cursor < capacity {
self.sector_index = self.meta.next(self.sector_index).await?;
}
self.meta.metadata.set_length(self.size);
Ok(length)
}
pub async fn write_all(&mut self, bytes: &[u8]) -> Result<(), Error<E>> {
let written = self.write(bytes).await?; for chunk in bytes[written..].chunks(self.meta.fs.sector_size() as usize) {
self.write(chunk).await?;
}
Ok(())
}
pub async fn sync_data(&mut self) -> Result<(), Error<E>> {
if self.dirty {
self.meta.io.acquire().await.wrap().flush().await?;
self.dirty = false;
}
Ok(())
}
pub async fn sync_all(&mut self) -> Result<(), Error<E>> {
self.sync_data().await?;
self.meta.sync().await
}
pub async fn flush(&mut self) -> Result<(), Error<E>> {
self.sync_all().await
}
pub async fn seek(&mut self, seek_from: SeekFrom) -> Result<u64, Error<E>> {
let option = match seek_from {
SeekFrom::Start(cursor) => i64::try_from(cursor).ok(),
SeekFrom::End(offset) => Some((self.cursor as i64) + offset),
SeekFrom::Current(offset) => (self.cursor as i64).checked_add(offset),
};
let cursor = option.ok_or(Error::Input(InputError::SeekPosition))?;
if cursor < 0 || cursor >= self.size as i64 {
return Err(InputError::SeekPosition.into());
}
let cursor = cursor as u64;
let sector_size = self.meta.fs.sector_size() as u64;
let num_sectors = match () {
_ if cursor > self.cursor => (cursor - self.cursor + sector_size - 1) / sector_size,
_ if cursor < self.cursor => {
self.sector_index = self.meta.sector_index;
(cursor + sector_size - 1) / sector_size
}
_ => 0,
};
for _ in 0..num_sectors {
self.sector_index = self.meta.next(self.sector_index).await?;
}
self.cursor = cursor;
Ok(cursor)
}
pub async fn truncate(&mut self, size: u64) -> Result<(), Error<E>> {
if size > self.size {
return Err(InputError::Size.into());
}
if self.cursor > size {
self.cursor = size;
self.seek(SeekFrom::Start(size)).await?;
}
self.meta.metadata.set_length(size);
self.size = size;
Ok(())
}
#[cfg(all(feature = "async", not(feature = "std")))]
pub async fn close(mut self) -> Result<(), Error<E>> {
self.closed = true;
self.flush().await.and(self.meta.close().await)
}
}
#[cfg(any(not(feature = "async"), feature = "std"))]
impl<B: Deref<Target = [Block]>, E: Debug, IO> Drop for File<B, E, IO>
where
IO: io::IO<Block<'static> = B, Error = E>,
{
fn drop(&mut self) {
#[cfg(feature = "async")]
if !self.closed {
panic!("Close must be explicitly called")
}
#[cfg(not(feature = "async"))]
self.flush().and(self.meta.close()).unwrap();
}
}