ableos/ext2-rs/src/volume/mod.rs

372 lines
10 KiB
Rust

#![allow(missing_docs)]
use {
alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
vec::Vec,
},
core::{
mem,
ops::{Deref, DerefMut, Range},
slice,
},
error::Error,
sector::{Address, SectorSize},
};
pub mod size;
use self::size::Size;
pub trait Volume<T: Clone, S: SectorSize> {
type Error: Into<Error>;
fn size(&self) -> Size<S>;
fn commit(
&mut self,
slice: Option<VolumeCommit<T, S>>,
) -> Result<(), Self::Error>;
unsafe fn slice_unchecked<'a>(
&'a self,
range: Range<Address<S>>,
) -> VolumeSlice<'a, T, S>;
fn slice<'a>(
&'a self,
range: Range<Address<S>>,
) -> Result<VolumeSlice<'a, T, S>, Self::Error>;
}
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct VolumeSlice<'a, T: 'a + Clone, S: SectorSize> {
inner: Cow<'a, [T]>,
index: Address<S>,
}
impl<T: Clone, S: SectorSize> VolumeSlice<'static, T, S> {
pub fn with_static(inner: &'static [T]) -> VolumeSlice<'static, T, S> {
VolumeSlice {
inner: Cow::Borrowed(inner),
index: Address::new(0, 0),
}
}
pub fn new_owned(
inner: <[T] as ToOwned>::Owned,
index: Address<S>,
) -> VolumeSlice<'static, T, S> {
VolumeSlice {
inner: Cow::Owned(inner),
index,
}
}
}
impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> {
pub fn new(inner: &'a [T], index: Address<S>) -> VolumeSlice<'a, T, S> {
VolumeSlice {
inner: Cow::Borrowed(inner),
index,
}
}
pub fn is_mutated(&self) -> bool {
match self.inner {
Cow::Borrowed(_) => false,
Cow::Owned(_) => true,
}
}
pub fn address(&self) -> Address<S> {
self.index
}
}
impl<'a, S: SectorSize> VolumeSlice<'a, u8, S> {
pub unsafe fn dynamic_cast<T: Copy>(&self) -> (T, Address<S>) {
assert!(self.inner.len() >= mem::size_of::<T>());
let index = self.index;
let cast = self.inner.as_ptr().cast::<T>().read_unaligned();
// mem::transmute_copy(self.inner.as_ptr().as_ref().unwrap());
(cast, index)
}
pub fn from_cast<T: Copy>(
cast: &'a T,
index: Address<S>,
) -> VolumeSlice<'a, u8, S> {
let len = mem::size_of::<T>();
let ptr = cast as *const T as *const u8;
let slice = unsafe { slice::from_raw_parts(ptr, len) };
VolumeSlice::new(slice, index)
}
}
impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> {
pub fn commit(self) -> Option<VolumeCommit<T, S>> {
if self.is_mutated() {
Some(VolumeCommit::new(self.inner.into_owned(), self.index))
} else {
None
}
}
}
impl<'a, T: Clone, S: SectorSize> AsRef<[T]> for VolumeSlice<'a, T, S> {
fn as_ref(&self) -> &[T] {
self.inner.as_ref()
}
}
impl<'a, T: Clone, S: SectorSize> AsMut<[T]> for VolumeSlice<'a, T, S> {
fn as_mut(&mut self) -> &mut [T] {
self.inner.to_mut().as_mut()
}
}
impl<'a, T: Clone, S: SectorSize> Deref for VolumeSlice<'a, T, S> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a, T: Clone, S: SectorSize> DerefMut for VolumeSlice<'a, T, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
pub struct VolumeCommit<T, S: SectorSize> {
inner: Vec<T>,
index: Address<S>,
}
impl<T: Clone, S: SectorSize> VolumeCommit<T, S> {
pub fn with_vec(inner: Vec<T>) -> VolumeCommit<T, S> {
VolumeCommit {
inner,
index: Address::new(0, 0),
}
}
}
impl<T: Clone, S: SectorSize> VolumeCommit<T, S> {
pub fn new(inner: Vec<T>, index: Address<S>) -> VolumeCommit<T, S> {
VolumeCommit { inner, index }
}
pub fn into_inner(self) -> Vec<T> {
self.inner
}
pub fn address(&self) -> Address<S> {
self.index
}
}
impl<T: Clone, S: SectorSize> AsRef<[T]> for VolumeCommit<T, S> {
fn as_ref(&self) -> &[T] {
self.inner.as_ref()
}
}
impl<T: Clone, S: SectorSize> AsMut<[T]> for VolumeCommit<T, S> {
fn as_mut(&mut self) -> &mut [T] {
self.inner.as_mut()
}
}
impl<T: Clone, S: SectorSize> Deref for VolumeCommit<T, S> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<T: Clone, S: SectorSize> DerefMut for VolumeCommit<T, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
macro_rules! impl_slice {
(@inner $volume:ty $( , $lt:lifetime )* ) => {
impl<$( $lt, )* T: Clone, S: SectorSize> Volume<T, S>
for $volume
{
type Error = Error;
fn size(&self) -> Size<S> {
Size::Bounded(
Address::from(<Self as AsRef<[T]>>::as_ref(self).len())
)
}
fn commit(
&mut self,
slice: Option<VolumeCommit<T, S>>,
) -> Result<(), Self::Error> {
slice.map(|slice| {
let index = slice.address().into_index() as usize;
let end = index + slice.as_ref().len();
// XXX: it would be much better to drop the contents of dst
// and move the contents of slice instead of cloning
let dst =
&mut <Self as AsMut<[T]>>::as_mut(self)[index..end];
dst.clone_from_slice(slice.as_ref());
});
Ok(())
}
unsafe fn slice_unchecked<'a>(
&'a self,
range: Range<Address<S>>,
) -> VolumeSlice<'a, T, S> {
let index = range.start;
let range = range.start.into_index() as usize
..range.end.into_index() as usize;
VolumeSlice::new(
<Self as AsRef<[T]>>::as_ref(self).get_unchecked(range),
index,
)
}
fn slice<'a>(
&'a self,
range: Range<Address<S>>,
) -> Result<VolumeSlice<'a, T, S>, Self::Error> {
if self.size() >= range.end {
unsafe { Ok(self.slice_unchecked(range)) }
} else {
Err(Error::AddressOutOfBounds {
sector: range.end.sector(),
offset: range.end.offset(),
size: range.end.sector_size()
})
}
}
}
};
($volume:ty) => {
impl_slice!(@inner $volume);
};
($volume:ty $( , $lt:lifetime )* ) => {
impl_slice!(@inner $volume $( , $lt )* );
};
}
impl_slice!(&'b mut [T], 'b);
impl_slice!(Vec<T>);
impl_slice!(Box<[T]>);
#[cfg(any(test, not(feature = "no_std")))]
mod file {
use std::cell::RefCell;
use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::Range;
use sector::{Address, SectorSize};
use super::size::Size;
use super::{Volume, VolumeCommit, VolumeSlice};
impl<S: SectorSize> Volume<u8, S> for RefCell<File> {
type Error = io::Error;
fn size(&self) -> Size<S> {
Size::Bounded(
self.borrow()
.metadata()
.map(|data| Address::from(data.len()))
.unwrap_or(Address::new(0, 0)),
)
}
fn commit(
&mut self,
slice: Option<VolumeCommit<u8, S>>,
) -> Result<(), Self::Error> {
slice
.map(|slice| {
let index = slice.address();
let mut refmut = self.borrow_mut();
refmut
.seek(SeekFrom::Start(index.into_index()))
.and_then(|_| refmut.write(slice.as_ref()))
.map(|_| ())
})
.unwrap_or(Ok(()))
}
unsafe fn slice_unchecked<'a>(
&'a self,
range: Range<Address<S>>,
) -> VolumeSlice<'a, u8, S> {
let index = range.start;
let len = range.end - range.start;
let mut vec = Vec::with_capacity(len.into_index() as usize);
vec.set_len(len.into_index() as usize);
let mut refmut = self.borrow_mut();
refmut
.seek(SeekFrom::Start(index.into_index()))
.and_then(|_| refmut.read_exact(&mut vec[..]))
.unwrap_or_else(|err| {
panic!("could't read from File Volume: {:?}", err)
});
VolumeSlice::new_owned(vec, index)
}
fn slice<'a>(
&'a self,
range: Range<Address<S>>,
) -> Result<VolumeSlice<'a, u8, S>, Self::Error> {
let index = range.start;
let mut vec = Vec::with_capacity(
(range.end - range.start).into_index() as usize,
);
unsafe {
vec.set_len((range.end - range.start).into_index() as usize);
}
let mut refmut = self.borrow_mut();
refmut
.seek(SeekFrom::Start(index.into_index()))
.and_then(|_| refmut.read_exact(&mut vec[..]))
.map(move |_| VolumeSlice::new_owned(vec, index))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use sector::{Address, Size512};
#[test]
fn volume() {
let mut volume = vec![0; 1024];
let commit = {
let mut slice = volume
.slice(
Address::<Size512>::from(256_u64)
..Address::<Size512>::from(512_u64),
)
.unwrap();
slice.iter_mut().for_each(|x| *x = 1);
slice.commit()
};
assert!(volume.commit(commit).is_ok());
for (i, &x) in volume.iter().enumerate() {
if i < 256 || i >= 512 {
assert_eq!(x, 0);
} else {
assert_eq!(x, 1);
}
}
}
}