use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use deku::no_std_io::{Read, Seek, SeekFrom, Write};
use crate::arch::{u32_to_usize, usize_to_u64};
use crate::celled::Celled;
use crate::dev::Device;
use crate::dev::address::Address;
pub trait Block<Dev: Device> {
type Num: Into<u64>;
fn size(&self) -> u32;
fn number(&self) -> Self::Num;
fn device(&mut self) -> Celled<Dev>;
}
pub struct BlockWrapper<Dev: Device, B: Block<Dev>> {
inner: B,
io_offset: u64,
phantom: PhantomData<Dev>,
}
impl<Dev: Device, B: Block<Dev>> const Deref for BlockWrapper<Dev, B> {
type Target = B;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<Dev: Device, B: Block<Dev>> const DerefMut for BlockWrapper<Dev, B> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<Dev: Device, B: Block<Dev>> Read for BlockWrapper<Dev, B> {
fn read(&mut self, buf: &mut [u8]) -> deku::no_std_io::Result<usize> {
let binder = self.device();
let mut device = binder.lock();
let length = (u32_to_usize(self.inner.size())
- unsafe { usize::try_from(self.io_offset & usize_to_u64(usize::MAX)).unwrap_unchecked() })
.min(buf.len());
let starting_addr =
Address::new(Into::<u64>::into(self.number()) * u64::from(self.inner.size()) + self.io_offset);
let slice = device.slice(starting_addr..starting_addr + usize_to_u64(length))?;
buf.clone_from_slice(slice.as_ref());
self.io_offset += usize_to_u64(length);
Ok(length)
}
}
impl<Dev: Device, B: Block<Dev>> Write for BlockWrapper<Dev, B> {
fn write(&mut self, buf: &[u8]) -> deku::no_std_io::Result<usize> {
let binder = self.device();
let mut device = binder.lock();
let length = (u32_to_usize(self.inner.size())
- unsafe { usize::try_from(self.io_offset & usize_to_u64(usize::MAX)).unwrap_unchecked() })
.min(buf.len());
let starting_addr =
Address::new(Into::<u64>::into(self.number()) * u64::from(self.inner.size()) + self.io_offset);
let mut slice = device.slice(starting_addr..starting_addr + usize_to_u64(length))?;
slice.clone_from_slice(unsafe { buf.get_unchecked(..length) });
let commit = slice.commit();
device.commit(commit)?;
self.io_offset += usize_to_u64(length);
Ok(length)
}
fn flush(&mut self) -> deku::no_std_io::Result<()> {
Ok(())
}
}
impl<Dev: Device, B: Block<Dev>> Seek for BlockWrapper<Dev, B> {
fn seek(&mut self, pos: SeekFrom) -> deku::no_std_io::Result<u64> {
let block_size = i64::from(self.inner.size());
let previous_offset = self.io_offset;
match pos {
SeekFrom::Start(offset) => self.io_offset = offset,
SeekFrom::End(back_offset) => {
self.io_offset = u64::try_from(block_size + back_offset).map_err(|_err| {
deku::no_std_io::Error::new(
deku::no_std_io::ErrorKind::InvalidInput,
"Invalid seek to a negative or overflowing position",
)
})?;
},
SeekFrom::Current(add_offset) => {
self.io_offset = (i64::try_from(previous_offset)
.expect("The I/O offset of a block is in an incoherent state")
+ add_offset)
.try_into()
.map_err(|_err| {
deku::no_std_io::Error::new(
deku::no_std_io::ErrorKind::InvalidInput,
"Invalid seek to a negative or overflowing position",
)
})?;
},
}
if self.io_offset >= u64::from(self.inner.size()) {
Err(deku::no_std_io::Error::new(
deku::no_std_io::ErrorKind::InvalidInput,
"Invalid seek to a negative or overflowing position",
))
} else {
Ok(previous_offset)
}
}
}
impl<Dev: Device, B: Block<Dev>> BlockWrapper<Dev, B> {
pub const fn new_wrapper(block: B) -> Self {
Self {
inner: block,
io_offset: 0,
phantom: PhantomData,
}
}
}