use alloc::borrow::{Cow, ToOwned};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::iter::Step;
use core::mem::{size_of, transmute_copy};
use core::ops::{Deref, DerefMut, Range};
use core::ptr::{addr_of, slice_from_raw_parts};
use deku::no_std_io::{Read, Seek, SeekFrom, Write};
use deku::{DekuContainerRead, DekuContainerWrite};
use derive_more::{Constructor, Deref, DerefMut};
use self::address::Address;
use self::size::Size;
use crate::arch::usize_to_u64;
#[cfg(feature = "std")]
use crate::fs::types::Time;
use crate::fs::types::Timespec;
pub mod address;
pub mod size;
#[derive(Debug, Clone)]
pub struct Slice<'mem> {
inner: Cow<'mem, [u8]>,
starting_addr: Address,
}
impl AsRef<[u8]> for Slice<'_> {
fn as_ref(&self) -> &[u8] {
&self.inner
}
}
impl AsMut<[u8]> for Slice<'_> {
fn as_mut(&mut self) -> &mut [u8] {
self.inner.to_mut().as_mut()
}
}
impl Deref for Slice<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl DerefMut for Slice<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
impl<'mem> Slice<'mem> {
#[must_use]
pub const fn new(inner: &'mem [u8], starting_addr: Address) -> Self {
Self {
inner: Cow::Borrowed(inner),
starting_addr,
}
}
#[must_use]
pub const fn new_owned(inner: <[u8] as ToOwned>::Owned, starting_addr: Address) -> Self {
Self {
inner: Cow::Owned(inner),
starting_addr,
}
}
#[must_use]
pub const fn addr(&self) -> Address {
self.starting_addr
}
#[must_use]
pub const fn is_mutated(&self) -> bool {
match self.inner {
Cow::Borrowed(_) => false,
Cow::Owned(_) => true,
}
}
#[must_use]
pub fn commit(self) -> Commit {
Commit::new(self.inner.into_owned(), self.starting_addr)
}
}
impl<'mem> Slice<'mem> {
#[must_use]
pub unsafe fn cast<T: Copy>(&self) -> T {
assert!(
self.inner.len() >= size_of::<T>(),
"The length of the device slice is not great enough to contain an object T"
);
unsafe { transmute_copy(self.inner.as_ptr().as_ref().expect("Could not read the pointer of the slice")) }
}
pub const fn from<T: Copy>(object: T, starting_addr: Address) -> Self {
let len = size_of::<T>();
let ptr = addr_of!(object).cast::<u8>();
let inner_opt = unsafe { slice_from_raw_parts(ptr, len).as_ref::<'mem>() };
Self::new(unsafe { inner_opt.unwrap_unchecked() }, starting_addr)
}
}
#[derive(Debug, Clone)]
pub struct Commit {
inner: Vec<u8>,
starting_addr: Address,
}
impl Commit {
#[must_use]
pub const fn new(inner: Vec<u8>, starting_addr: Address) -> Self {
Self {
inner,
starting_addr,
}
}
#[must_use]
pub const fn addr(&self) -> Address {
self.starting_addr
}
}
impl AsRef<[u8]> for Commit {
fn as_ref(&self) -> &[u8] {
&self.inner
}
}
impl AsMut<[u8]> for Commit {
fn as_mut(&mut self) -> &mut [u8] {
self.inner.as_mut()
}
}
pub trait Device {
fn size(&mut self) -> Size;
fn slice(&mut self, addr_range: Range<Address>) -> deku::no_std_io::Result<Slice<'_>>;
fn commit(&mut self, commit: Commit) -> deku::no_std_io::Result<()>;
fn read_from_bytes<O: for<'a> DekuContainerRead<'a>>(
&mut self,
starting_addr: Address,
length: usize,
) -> deku::no_std_io::Result<O> {
let range = starting_addr..Address::forward_checked(starting_addr, length).ok_or_else(|| {
deku::no_std_io::Error::new(deku::no_std_io::ErrorKind::InvalidInput, "Tried to reach an invalid address")
})?;
let slice = self.slice(range)?;
O::from_bytes((&slice, 0)).map(|(_, obj)| obj).map_err(Into::into)
}
fn write_to_bytes<O: DekuContainerWrite>(&mut self, starting_addr: Address, obj: O) -> deku::no_std_io::Result<()> {
let obj_bytes = obj.to_bytes()?;
let length = obj_bytes.len();
let range = starting_addr..Address::forward_checked(starting_addr, length).ok_or_else(|| {
deku::no_std_io::Error::new(deku::no_std_io::ErrorKind::InvalidInput, "Tried to reach an invalid address")
})?;
let mut device_slice = self.slice(range)?;
let buffer = device_slice
.get_mut(..)
.unwrap_or_else(|| unreachable!("It is always possible to take all the elements of a slice"));
buffer.copy_from_slice(&obj_bytes);
let commit = device_slice.commit();
self.commit(commit)
}
fn now(&mut self) -> Option<Timespec> {
None
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[must_use]
pub fn std_now() -> Timespec {
let now = unsafe { std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_unchecked() };
Timespec {
tv_sec: Time(unsafe { now.as_secs().try_into().unwrap_unchecked() }),
tv_nsec: unsafe { u32::try_from(now.as_nanos() % 1_000_000_000).unwrap_unchecked() },
}
}
impl<T: Read + Write + Seek> Device for T {
fn size(&mut self) -> Size {
let offset = self.seek(SeekFrom::End(0)).expect("Could not seek the device at its end");
let size = self
.seek(SeekFrom::Start(offset))
.expect("Could not seek the device at its original offset");
Size(size)
}
fn slice(&mut self, addr_range: Range<Address>) -> deku::no_std_io::Result<Slice<'_>> {
let starting_addr = addr_range.start;
let len = TryInto::<usize>::try_into((addr_range.end - addr_range.start).index()).map_err(|_err| {
deku::no_std_io::Error::new(deku::no_std_io::ErrorKind::InvalidInput, "Tried to reach an invalid address")
})?;
let mut slice = alloc::vec![0; len];
self.seek(SeekFrom::Start(starting_addr.index()))?;
self.read_exact(&mut slice)?;
Ok(Slice::new_owned(slice, starting_addr))
}
fn commit(&mut self, commit: Commit) -> deku::no_std_io::Result<()> {
let offset = self.seek(SeekFrom::Start(commit.addr().index()))?;
self.write_all(commit.as_ref())?;
self.seek(SeekFrom::Start(offset))?;
Ok(())
}
#[cfg(feature = "std")]
fn now(&mut self) -> Option<Timespec> {
Some(std_now())
}
}
#[derive(Debug, Clone, Copy, Constructor, Default, Deref, DerefMut)]
pub struct Wrapper<T>(T);
macro_rules! impl_device {
($volume:ty) => {
impl Device for Wrapper<$volume> {
fn size(&mut self) -> Size {
Size(usize_to_u64(self.len()))
}
fn slice(&mut self, addr_range: Range<Address>) -> deku::no_std_io::Result<Slice<'_>> {
if Device::size(self) >= u64::from(addr_range.end) {
let addr_start = addr_range.start;
let range = usize::try_from(addr_range.start.index()).expect(
"Unreachable: tried to handle a structure that need more RAM that the system can handle",
)
..usize::try_from(addr_range.end.index()).expect(
"Unreachable: tried to handle a structure that need more RAM that the system can handle",
);
Ok(Slice::new(unsafe { <$volume as AsRef<[u8]>>::as_ref(self).get_unchecked(range) }, addr_start))
} else {
Err(
deku::no_std_io::Error::new(deku::no_std_io::ErrorKind::InvalidInput, "Tried to reach an invalid address")
)
}
}
fn commit(&mut self, commit: Commit) -> deku::no_std_io::Result<()> {
let addr_start = commit.addr().index();
let addr_end = addr_start + usize_to_u64(commit.as_ref().len());
let dest = &mut <$volume as AsMut<[u8]>>::as_mut(self).get_mut(usize::try_from(addr_start).expect(
"Unreachable: tried to handle a structure that need more RAM that the system can handle",
)
..usize::try_from(addr_end).expect(
"Unreachable: tried to handle a structure that need more RAM that the system can handle",
)).ok_or_else(|| {
deku::no_std_io::Error::new(deku::no_std_io::ErrorKind::InvalidInput, "Tried to reach an invalid address")
})?;
dest.clone_from_slice(&commit.as_ref());
Ok(())
}
}
};
}
impl_device!(&mut [u8]);
impl_device!(Vec<u8>);
impl_device!(Box<[u8]>);
#[cfg(test)]
mod test {
use alloc::string::String;
use alloc::vec;
use std::fs::{self, File};
use deku::no_std_io::{Read, Seek, SeekFrom, Write};
use deku::{DekuContainerWrite, DekuRead, DekuWrite};
use crate::dev::address::Address;
use crate::dev::{Device, Wrapper};
#[test]
fn device_generic_read() {
let mut device = Wrapper::new(vec![0_u8; 1024]);
let mut slice = device.slice(Address::from(256_u32)..Address::from(512_u32)).unwrap();
slice.iter_mut().for_each(|element| *element = 1);
let commit = slice.commit();
assert!(device.commit(commit).is_ok());
for (idx, &x) in device.iter().enumerate() {
assert_eq!(x, u8::from((256..512).contains(&idx)));
}
}
#[allow(clippy::missing_asserts_for_indexing)]
fn device_file_write(mut file_1: File) {
let mut slice = file_1.slice(Address::new(0)..Address::new(13)).unwrap();
let word = slice.get_mut(6..=10).unwrap();
word[0] = b'e';
word[1] = b'a';
word[2] = b'r';
word[3] = b't';
word[4] = b'h';
let commit = slice.commit();
file_1.commit(commit).unwrap();
std::io::Seek::rewind(&mut file_1).unwrap();
let mut file_1_content = String::new();
std::io::Read::read_to_string(&mut file_1, &mut file_1_content).unwrap();
let file_2_content = String::from_utf8(fs::read("./tests/dev/device_file_2.txt").unwrap()).unwrap();
assert_eq!(file_1_content, file_2_content);
}
#[allow(clippy::struct_field_names)]
#[test]
fn device_generic_read_from_bytes() {
const OFFSET: usize = 0xA0;
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite)]
#[deku(endian = "little")]
struct Test {
nb_1: u16,
nb_2: u8,
nb_3: usize,
nb_4: u128,
}
let test = Test {
nb_1: 0xabcd,
nb_2: 0x99,
nb_3: 0x1234,
nb_4: 0x1234_5678_90ab_cdef,
};
let test_bytes = test.to_bytes().unwrap();
let mut device = Wrapper::new(vec![0_u8; 1024]);
let mut slice = device.slice(Address::from(OFFSET)..Address::from(OFFSET + test_bytes.len())).unwrap();
let buffer = slice.get_mut(..).unwrap();
buffer.clone_from_slice(&test_bytes);
let commit = slice.commit();
device.commit(commit).unwrap();
let read_test = device.read_from_bytes::<Test>(Address::from(OFFSET), 32).unwrap();
assert_eq!(test, read_test);
}
#[allow(clippy::struct_field_names)]
#[test]
fn device_generic_write_to_bytes() {
const OFFSET: u64 = 123;
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite)]
struct Test {
nb_1: u16,
nb_2: u8,
nb_3: usize,
nb_4: u128,
}
let test = Test {
nb_1: 0xabcd,
nb_2: 0x99,
nb_3: 0x1234,
nb_4: 0x1234_5678_90ab_cdef,
};
let test_bytes = test.to_bytes().unwrap();
let mut device = Wrapper::new(vec![0_u8; 1024]);
device.write_to_bytes(Address::from(OFFSET), test).unwrap();
let slice = device
.slice(Address::from(OFFSET)..Address::from(OFFSET + test_bytes.len() as u64))
.unwrap();
assert_eq!(test_bytes, slice.as_ref());
}
#[test]
fn dummy_device() {
struct Foo {}
impl Read for Foo {
fn read(&mut self, buf: &mut [u8]) -> deku::no_std_io::Result<usize> {
buf.fill(1);
Ok(buf.len())
}
}
impl Write for Foo {
fn write(&mut self, buf: &[u8]) -> deku::no_std_io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> deku::no_std_io::Result<()> {
Ok(())
}
}
impl Seek for Foo {
fn seek(&mut self, _pos: SeekFrom) -> deku::no_std_io::Result<u64> {
Ok(0)
}
}
#[derive(Debug, PartialEq, Eq, DekuRead)]
struct Bar {
a: u8,
b: u32,
}
let mut device = Foo {};
assert_eq!(device.read_from_bytes::<Bar>(Address::new(0), 5).unwrap(), Bar {
a: 0x1,
b: 0x0101_0101
});
}
mod generated {
use crate::tests::generate_fs_test;
generate_fs_test!(device_file_write, "./tests/dev/device_file_1.txt");
}
}