use crate::ASCII_SPACE;
pub struct File<'a, const BLOCK_SIZE: usize = 512> {
pub(crate) name: &'a str,
pub(crate) data: FileContent<'a, BLOCK_SIZE>,
}
pub enum FileContent<'a, const BLOCK_SIZE: usize = 512> {
Read(&'a [u8]),
Write(&'a mut [u8]),
Dynamic(&'a mut dyn DynamicFile<BLOCK_SIZE>),
}
pub trait DynamicFile<const BLOCK_SIZE: usize = 512>: Sync + Send {
fn len(&self) -> usize;
fn read_chunk(&self, chunk_index: usize, buff: &mut [u8]) -> usize;
fn write_chunk(&mut self, chunk_index: usize, data: &[u8]) -> usize;
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FileError {
InvalidName,
}
bitflags::bitflags! {
pub struct Attrs: u8 {
const READ_ONLY = 0x01;
const HIDDEN = 0x02;
const SYSTEM = 0x04;
const VOLUME_LABEL=0x08;
const SUBDIR = 0x10;
const ARCHIVE = 0x20;
const DEVICE = 0x40;
}
}
impl <'a, const BLOCK_SIZE: usize>From<&'a [u8]> for FileContent<'a, BLOCK_SIZE> {
fn from(d: &'a [u8]) -> Self {
FileContent::Read(d)
}
}
impl <'a, const BLOCK_SIZE: usize, const N: usize>From<&'a [u8; N]> for FileContent<'a, BLOCK_SIZE> {
fn from(d: &'a [u8; N]) -> Self {
FileContent::Read(d.as_ref())
}
}
impl <'a, const BLOCK_SIZE: usize>From<&'a mut [u8]> for FileContent<'a, BLOCK_SIZE> {
fn from(d: &'a mut [u8]) -> Self {
FileContent::Write(d)
}
}
impl <'a, const BLOCK_SIZE: usize, const N: usize>From<&'a mut [u8; N]> for FileContent<'a, BLOCK_SIZE> {
fn from(d: &'a mut [u8; N]) -> Self {
FileContent::Write(d.as_mut())
}
}
impl <'a, const BLOCK_SIZE: usize> File<'a, BLOCK_SIZE> {
pub fn new<D: Into<FileContent<'a, BLOCK_SIZE>>>(name: &'a str, data: D) -> Result<Self, FileError> {
let f = Self {
name,
data: data.into(),
};
f.short_name()?;
Ok(f)
}
pub const fn new_ro(name: &'a str, data: &'a [u8]) -> Self {
Self{ name, data: FileContent::Read(data) }
}
#[cfg(feature="nightly")]
pub const fn new_rw(name: &'a str, data: &'a mut [u8]) -> Self {
Self{ name, data: FileContent::Write(data) }
}
#[cfg(feature="nightly")]
pub const fn new_dyn(name: &'a str, data: &'a mut dyn DynamicFile<BLOCK_SIZE>) -> Self {
Self{ name, data: FileContent::Dynamic(data) }
}
pub fn name(&self) -> &str {
self.name
}
pub(crate) fn short_name(&self) -> Result<[u8; 11], FileError> {
let mut n = self.name.split(".");
let (prefix, ext) = match (n.next(), n.next()) {
(Some(p), Some(e)) => (p, e),
_ => return Err(FileError::InvalidName),
};
if prefix.len() + ext.len() > 11 {
return Err(FileError::InvalidName);
}
let mut short_name = [ASCII_SPACE; 11];
short_name[..prefix.len()].copy_from_slice(prefix.as_bytes());
short_name[11 - ext.len()..].copy_from_slice(ext.as_bytes());
Ok(short_name)
}
pub fn len(&self) -> usize {
match &self.data {
FileContent::Read(r) => r.len(),
FileContent::Write(w) => w.len(),
FileContent::Dynamic(rw) => rw.len(),
}
}
pub(crate) fn num_blocks(&self) -> usize {
let mut blocks = self.len() / BLOCK_SIZE;
if self.len() % BLOCK_SIZE != 0 {
blocks += 1;
}
blocks
}
pub(crate) fn attrs(&self) -> Attrs {
match &self.data {
FileContent::Read(_r) => Attrs::READ_ONLY,
FileContent::Write(_w) => Attrs::empty(),
FileContent::Dynamic(_rw) => Attrs::empty(),
}
}
pub(crate) fn chunk(&self, index: usize, buff: &mut [u8]) -> usize {
if let FileContent::Dynamic(rw) = &self.data {
return rw.read_chunk(index, buff)
}
let d = match &self.data {
FileContent::Read(r) => r.chunks(BLOCK_SIZE).nth(index),
FileContent::Write(w) => w.chunks(BLOCK_SIZE).nth(index),
_ => unreachable!(),
};
if let Some(d) = d {
let len = usize::min(buff.len(), d.len());
buff[..len].copy_from_slice(&d[..len]);
return len;
}
return 0;
}
pub(crate) fn chunk_mut(&mut self, index: usize, data: &[u8]) -> usize {
match &mut self.data {
FileContent::Read(_r) => return 0,
FileContent::Write(w) => {
if let Some(b) = w.chunks_mut(BLOCK_SIZE).nth(index) {
let len = usize::min(b.len(), data.len());
b[..len].copy_from_slice(&data[..len]);
return len;
}
},
FileContent::Dynamic(rw) => return rw.write_chunk(index, data),
}
return 0
}
}
pub struct ChunkIter {
offset: u32,
index: u32,
len: u32,
}
impl Iterator for ChunkIter {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.len {
let n = self.offset + self.index;
self.index += 1;
Some(n as u16)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
}