AA
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "ramfs"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
spin = "*"
|
||||
vfs = { path = "../vfs" }
|
||||
error = { path = "../../error" }
|
||||
@@ -0,0 +1,146 @@
|
||||
use core::mem::{size_of, MaybeUninit};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use error::Errno;
|
||||
|
||||
pub const SIZE: usize = 4096;
|
||||
pub const ENTRY_COUNT: usize = SIZE / size_of::<usize>();
|
||||
|
||||
pub trait BlockAllocator {
|
||||
fn alloc(&self) -> *mut u8;
|
||||
unsafe fn free(&self, ptr: *mut u8);
|
||||
}
|
||||
|
||||
pub struct BlockRef<'a, A: BlockAllocator + Copy> {
|
||||
inner: Option<&'a mut [u8; SIZE]>,
|
||||
alloc: MaybeUninit<A>,
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> BlockRef<'a, A> {
|
||||
pub fn new(alloc: A) -> Result<Self, Errno> {
|
||||
assert!(size_of::<A>() == 0);
|
||||
let ptr = alloc.alloc();
|
||||
if ptr.is_null() {
|
||||
Err(Errno::OutOfMemory)
|
||||
} else {
|
||||
Ok(unsafe { Self::from_raw(alloc, ptr) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_indirect(alloc: A) -> Result<Self, Errno> {
|
||||
let mut res = Self::new(alloc)?;
|
||||
for it in res.as_mut_ref_array().iter_mut() {
|
||||
it.write(BlockRef::null());
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn null() -> Self {
|
||||
Self {
|
||||
inner: None,
|
||||
alloc: MaybeUninit::uninit(),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn from_raw(alloc: A, data: *mut u8) -> Self {
|
||||
Self {
|
||||
inner: Some(&mut *(data as *mut _)),
|
||||
alloc: MaybeUninit::new(alloc),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
self.inner.is_none()
|
||||
}
|
||||
|
||||
pub fn as_mut_ref_array(&mut self) -> &mut [MaybeUninit<BlockRef<'a, A>>; ENTRY_COUNT] {
|
||||
assert_eq!(size_of::<Self>(), 8);
|
||||
unsafe { &mut *(self.deref_mut() as *mut _ as *mut _) }
|
||||
}
|
||||
|
||||
pub fn as_ref_array(&self) -> &[MaybeUninit<BlockRef<'a, A>>; ENTRY_COUNT] {
|
||||
assert_eq!(size_of::<Self>(), 8);
|
||||
unsafe { &*(self.deref() as *const _ as *const _) }
|
||||
}
|
||||
|
||||
pub fn zero(&mut self) {
|
||||
if let Some(inner) = self.inner.as_mut() {
|
||||
inner.fill(0);
|
||||
} else {
|
||||
panic!("Tried to fill a NULL blockref");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> Drop for BlockRef<'a, A> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = self.inner.take() {
|
||||
unsafe {
|
||||
self.alloc.assume_init_ref().free(inner as *mut _ as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> Deref for BlockRef<'a, A> {
|
||||
type Target = [u8; SIZE];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> DerefMut for BlockRef<'a, A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.inner.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::boxed::Box;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static A_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[test]
|
||||
fn block_allocator() {
|
||||
#[derive(Clone, Copy)]
|
||||
struct A;
|
||||
impl BlockAllocator for A {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::<[u8; SIZE]>::new_uninit());
|
||||
A_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
|
||||
unsafe fn free(&self, ptr: *mut u8) {
|
||||
A_COUNTER.fetch_sub(1, Ordering::SeqCst);
|
||||
drop(Box::from_raw(ptr as *mut [u8; SIZE]));
|
||||
}
|
||||
}
|
||||
|
||||
const N: usize = 13;
|
||||
{
|
||||
let mut s: [MaybeUninit<BlockRef<A>>; N] = MaybeUninit::uninit_array();
|
||||
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
|
||||
for i in 0..N {
|
||||
let mut block = BlockRef::new(A {}).unwrap();
|
||||
block.fill(1);
|
||||
s[i].write(block);
|
||||
}
|
||||
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), N);
|
||||
|
||||
for i in 0..N {
|
||||
unsafe {
|
||||
s[i].assume_init_drop();
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,691 @@
|
||||
use crate::{block, BlockAllocator, BlockRef};
|
||||
use core::cmp::{max, min};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use error::Errno;
|
||||
|
||||
const L0_BLOCKS: usize = 32; // 128K
|
||||
const L1_BLOCKS: usize = 8; // 16M
|
||||
|
||||
pub struct Bvec<'a, A: BlockAllocator + Copy> {
|
||||
capacity: usize,
|
||||
size: usize,
|
||||
l0: [MaybeUninit<BlockRef<'a, A>>; L0_BLOCKS],
|
||||
l1: [MaybeUninit<BlockRef<'a, A>>; L1_BLOCKS],
|
||||
l2: MaybeUninit<BlockRef<'a, A>>,
|
||||
alloc: A,
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> {
|
||||
pub fn new(alloc: A) -> Self {
|
||||
let mut res = Self {
|
||||
capacity: 0,
|
||||
size: 0,
|
||||
l0: MaybeUninit::uninit_array(),
|
||||
l1: MaybeUninit::uninit_array(),
|
||||
l2: MaybeUninit::uninit(),
|
||||
alloc,
|
||||
};
|
||||
for it in res.l0.iter_mut() {
|
||||
it.write(BlockRef::null());
|
||||
}
|
||||
for it in res.l1.iter_mut() {
|
||||
it.write(BlockRef::null());
|
||||
}
|
||||
res.l2.write(BlockRef::null());
|
||||
res
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, cap: usize) -> Result<(), Errno> {
|
||||
if cap <= self.capacity {
|
||||
let mut curr = self.capacity;
|
||||
while curr != cap {
|
||||
curr -= 1;
|
||||
let mut index = curr;
|
||||
|
||||
if index >= L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT {
|
||||
index -= L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT;
|
||||
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
|
||||
let l2r = unsafe { self.l2.assume_init_mut() };
|
||||
assert!(!l2r.is_null());
|
||||
let l1r = unsafe { l2r.as_mut_ref_array()[l1i].assume_init_mut() };
|
||||
assert!(!l1r.is_null());
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(!l0r.is_null());
|
||||
|
||||
*l0r = BlockRef::null();
|
||||
if l0i == 0 {
|
||||
*l1r = BlockRef::null();
|
||||
}
|
||||
if index == 0 {
|
||||
*l2r = BlockRef::null();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if index >= L0_BLOCKS {
|
||||
index -= L0_BLOCKS;
|
||||
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
|
||||
let l1r = unsafe { self.l1[l1i].assume_init_mut() };
|
||||
assert!(!l1r.is_null());
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(!l0r.is_null());
|
||||
|
||||
*l0r = BlockRef::null();
|
||||
if l0i == 0 {
|
||||
*l1r = BlockRef::null();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let l0r = unsafe { self.l0[index].assume_init_mut() };
|
||||
assert!(!l0r.is_null());
|
||||
*l0r = BlockRef::null();
|
||||
continue;
|
||||
|
||||
unimplemented!();
|
||||
}
|
||||
} else {
|
||||
for mut index in self.capacity..cap {
|
||||
if index < L0_BLOCKS {
|
||||
let l0r = unsafe { self.l0[index].assume_init_mut() };
|
||||
assert!(l0r.is_null());
|
||||
*l0r = BlockRef::new(self.alloc)?;
|
||||
continue;
|
||||
}
|
||||
index -= L0_BLOCKS;
|
||||
if index < L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
|
||||
let l1r = unsafe { self.l1[l1i].assume_init_mut() };
|
||||
if l1r.is_null() {
|
||||
*l1r = BlockRef::new_indirect(self.alloc)?;
|
||||
}
|
||||
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(l0r.is_null());
|
||||
*l0r = BlockRef::new(self.alloc)?;
|
||||
|
||||
continue;
|
||||
}
|
||||
index -= L1_BLOCKS * block::ENTRY_COUNT;
|
||||
if index < block::ENTRY_COUNT * block::ENTRY_COUNT {
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
|
||||
let l2r = unsafe { self.l2.assume_init_mut() };
|
||||
if l2r.is_null() {
|
||||
*l2r = BlockRef::new_indirect(self.alloc)?;
|
||||
}
|
||||
|
||||
let l1r = unsafe { l2r.as_mut_ref_array()[l1i].assume_init_mut() };
|
||||
if l1r.is_null() {
|
||||
*l1r = BlockRef::new_indirect(self.alloc)?;
|
||||
}
|
||||
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(l0r.is_null());
|
||||
*l0r = BlockRef::new(self.alloc)?;
|
||||
|
||||
continue;
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
self.capacity = cap;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(&mut self, mut pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
if pos > self.size {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut rem = data.len();
|
||||
let mut doff = 0usize;
|
||||
|
||||
if pos + rem > self.size {
|
||||
self.size = pos + rem;
|
||||
self.resize((pos + rem + block::SIZE - 1) / block::SIZE)?;
|
||||
}
|
||||
|
||||
while rem > 0 {
|
||||
let index = pos / block::SIZE;
|
||||
let off = pos % block::SIZE;
|
||||
let count = min(block::SIZE - off, rem);
|
||||
|
||||
let block = &mut self[index];
|
||||
|
||||
let dst = &mut block[off..off + count];
|
||||
let src = &data[doff..doff + count];
|
||||
dst.copy_from_slice(src);
|
||||
|
||||
doff += count;
|
||||
pos += count;
|
||||
rem -= count;
|
||||
}
|
||||
|
||||
Ok(doff)
|
||||
}
|
||||
|
||||
pub fn read(&self, mut pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
if pos > self.size {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut rem = min(self.size - pos, data.len());
|
||||
let mut doff = 0usize;
|
||||
|
||||
while rem > 0 {
|
||||
let index = pos / block::SIZE;
|
||||
let off = pos % block::SIZE;
|
||||
let count = min(block::SIZE - off, rem);
|
||||
|
||||
let block = &self[index];
|
||||
|
||||
let src = &block[off..off + count];
|
||||
let dst = &mut data[doff..doff + count];
|
||||
dst.copy_from_slice(src);
|
||||
|
||||
doff += count;
|
||||
pos += count;
|
||||
rem -= count;
|
||||
}
|
||||
|
||||
Ok(doff)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> Index<usize> for Bvec<'a, A> {
|
||||
type Output = BlockRef<'a, A>;
|
||||
fn index(&self, mut index: usize) -> &Self::Output {
|
||||
if index >= self.capacity {
|
||||
panic!(
|
||||
"Index exceeds bvec capacity ({} >= {})",
|
||||
index, self.capacity
|
||||
);
|
||||
}
|
||||
|
||||
if index < L0_BLOCKS {
|
||||
return unsafe { self.l0[index].assume_init_ref() };
|
||||
}
|
||||
index -= L0_BLOCKS;
|
||||
if index < L1_BLOCKS * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l1 = self.l1[index / block::ENTRY_COUNT].assume_init_ref();
|
||||
l1.as_ref_array()[index % block::ENTRY_COUNT].assume_init_ref()
|
||||
};
|
||||
}
|
||||
index -= L1_BLOCKS * block::ENTRY_COUNT;
|
||||
if index < block::ENTRY_COUNT * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l2 = self.l2.assume_init_ref();
|
||||
let l1 = l2.as_ref_array()[index / block::ENTRY_COUNT].assume_init_ref();
|
||||
l1.as_ref_array()[index % block::ENTRY_COUNT].assume_init_ref()
|
||||
};
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> IndexMut<usize> for Bvec<'a, A> {
|
||||
fn index_mut(&mut self, mut index: usize) -> &mut Self::Output {
|
||||
if index >= self.capacity {
|
||||
panic!(
|
||||
"Index exceeds bvec capacity ({} >= {})",
|
||||
index, self.capacity
|
||||
);
|
||||
}
|
||||
|
||||
if index < L0_BLOCKS {
|
||||
return unsafe { self.l0[index].assume_init_mut() };
|
||||
}
|
||||
index -= L0_BLOCKS;
|
||||
if index < L1_BLOCKS * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l1 = self.l1[index / block::ENTRY_COUNT].assume_init_mut();
|
||||
l1.as_mut_ref_array()[index % block::ENTRY_COUNT].assume_init_mut()
|
||||
};
|
||||
}
|
||||
index -= L1_BLOCKS * block::ENTRY_COUNT;
|
||||
if index < block::ENTRY_COUNT * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l2 = self.l2.assume_init_mut();
|
||||
let l1 = l2.as_mut_ref_array()[index / block::ENTRY_COUNT].assume_init_mut();
|
||||
l1.as_mut_ref_array()[index % block::ENTRY_COUNT].assume_init_mut()
|
||||
};
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> Drop for Bvec<'a, A> {
|
||||
fn drop(&mut self) {
|
||||
for i in 0..min(L0_BLOCKS, self.capacity) {
|
||||
unsafe {
|
||||
self.l0[i].assume_init_drop();
|
||||
}
|
||||
}
|
||||
if self.capacity > L0_BLOCKS {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test_bvec")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::boxed::Box;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static A_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct TestAlloc;
|
||||
impl BlockAllocator for TestAlloc {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::<[u8; block::SIZE]>::new_uninit());
|
||||
eprintln!("alloc {:p}", b);
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
|
||||
unsafe fn free(&self, ptr: *mut u8) {
|
||||
eprintln!("drop {:p}", ptr);
|
||||
drop(Box::from_raw(ptr as *mut [u8; block::SIZE]));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bvec_allocation() {
|
||||
#[derive(Clone, Copy)]
|
||||
struct A;
|
||||
impl BlockAllocator for A {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::<[u8; block::SIZE]>::new_uninit());
|
||||
A_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
|
||||
unsafe fn free(&self, ptr: *mut u8) {
|
||||
A_COUNTER.fetch_sub(1, Ordering::SeqCst);
|
||||
drop(Box::from_raw(ptr as *mut [u8; block::SIZE]));
|
||||
}
|
||||
}
|
||||
|
||||
let mut bvec = Bvec::new(A {});
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
|
||||
bvec.resize(123).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
let l1r = bvec.l1[0].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
for i in 0..123 - L0_BLOCKS {
|
||||
assert!(!l1r.as_ref_array()[i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 123 + 1);
|
||||
|
||||
bvec.resize(123 + block::ENTRY_COUNT).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..(123 + block::ENTRY_COUNT) - L0_BLOCKS {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
123 + block::ENTRY_COUNT + 2
|
||||
);
|
||||
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS
|
||||
);
|
||||
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 4)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
for i in 0..block::ENTRY_COUNT * 4 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = l2r.as_ref_array()[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
block::ENTRY_COUNT * 4 + 4 + 1
|
||||
);
|
||||
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 3 + 1)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
for i in 0..block::ENTRY_COUNT * 3 + 1 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = l2r.as_ref_array()[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
block::ENTRY_COUNT * 3 + 1 + 4 + 1
|
||||
);
|
||||
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 1)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
for i in 0..block::ENTRY_COUNT * 2 + 1 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = l2r.as_ref_array()[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
block::ENTRY_COUNT * 2 + 1 + 3 + 1
|
||||
);
|
||||
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 1)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
let l1r = l2r.as_ref_array()[0].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[0].assume_init_ref().is_null());
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
1 + 1 + 1
|
||||
);
|
||||
|
||||
bvec.resize(L0_BLOCKS + 3 * block::ENTRY_COUNT + 1).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..3 * block::ENTRY_COUNT + 1 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(l2r.is_null());
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
3 * block::ENTRY_COUNT + 1 + 4
|
||||
);
|
||||
|
||||
bvec.resize(L0_BLOCKS).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
assert!(bvec.l1[0].assume_init_ref().is_null());
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), L0_BLOCKS);
|
||||
|
||||
bvec.resize(12).unwrap();
|
||||
unsafe {
|
||||
for i in 0..12 {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 12);
|
||||
|
||||
bvec.resize(0).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bvec_index_l0() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS).unwrap();
|
||||
|
||||
for i in 0..L0_BLOCKS {
|
||||
let block = &bvec[i];
|
||||
assert_eq!(block as *const _, bvec.l0[i].as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bvec_index_l1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap();
|
||||
|
||||
for i in 0..block::ENTRY_COUNT * 2 + 3 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let block = &bvec[i + L0_BLOCKS];
|
||||
let l1r = unsafe { bvec.l1[l1i].assume_init_ref() };
|
||||
assert_eq!(block as *const _, l1r.as_ref_array()[l0i].as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bvec_index_l2() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3)
|
||||
.unwrap();
|
||||
|
||||
for i in 0..3 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let block = &bvec[i + L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT];
|
||||
let l2r = unsafe { bvec.l2.assume_init_ref() };
|
||||
let l1r = unsafe { l2r.as_ref_array()[l1i].assume_init_ref() };
|
||||
assert_eq!(block as *const _, l1r.as_ref_array()[l0i].as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l0_0() {
|
||||
let bvec = Bvec::new(TestAlloc {});
|
||||
let _block = &bvec[0];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l0_1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(13).unwrap();
|
||||
let _block = &bvec[15];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l1_0() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(13).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + 2];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l1_1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + block::ENTRY_COUNT * 2 + 6];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l1_2() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + block::ENTRY_COUNT * 3 + 1];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_0() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(13).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 3 + 13)
|
||||
.unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_2() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 6)
|
||||
.unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 8];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_3() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 7)
|
||||
.unwrap();
|
||||
let _block =
|
||||
&bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 13];
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_4() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 13)
|
||||
.unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 3 + 2];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bvec_write_read() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
const N: usize = block::SIZE * (L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3);
|
||||
let mut data = vec![0u8; N];
|
||||
for i in 0..N {
|
||||
data[i] = (i & 0xFF) as u8;
|
||||
}
|
||||
assert_eq!(bvec.write(0, &data[..]), Ok(N));
|
||||
|
||||
let mut buf = vec![0u8; 327];
|
||||
let mut off = 0usize;
|
||||
let mut rem = N;
|
||||
while rem != 0 {
|
||||
let count = min(rem, buf.len());
|
||||
assert_eq!(bvec.read(off, &mut buf[..]), Ok(count));
|
||||
|
||||
for i in 0..count {
|
||||
assert_eq!(buf[i], ((i + off) & 0xFF) as u8);
|
||||
}
|
||||
|
||||
rem -= count;
|
||||
off += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#![no_std]
|
||||
#![feature(new_uninit, maybe_uninit_uninit_array, maybe_uninit_extra)]
|
||||
|
||||
extern crate alloc;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use error::Errno;
|
||||
use spin::Mutex;
|
||||
use vfs::{Node, NodeRef, NodeType};
|
||||
|
||||
mod block;
|
||||
pub use block::{BlockAllocator, BlockRef};
|
||||
mod bvec;
|
||||
use bvec::Bvec;
|
||||
|
||||
pub struct Ramfs<'a> {
|
||||
root: NodeRef,
|
||||
allocator: &'a Mutex<dyn BlockAllocator>,
|
||||
}
|
||||
|
||||
pub struct NodeData {
|
||||
}
|
||||
|
||||
fn load_tar(base: *const u8, size: usize) -> Result<NodeRef, Errno> {
|
||||
let root = Node::directory(b"");
|
||||
// TODO
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
pub fn open(
|
||||
base: *const u8,
|
||||
size: usize,
|
||||
allocator: &Mutex<dyn BlockAllocator>,
|
||||
) -> Result<Ramfs, Errno> {
|
||||
let root = load_tar(base, size)?;
|
||||
|
||||
Ok(Ramfs { root, allocator })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ramfs_open() {
|
||||
let data = include_str!("../test/test0.tar");
|
||||
//let fs = open(data.as_ptr(), data.bytes().len()).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
This is a file
|
||||
File0
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "vfs"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
error = { path = "../../error" }
|
||||
spin = "*"
|
||||
@@ -0,0 +1,187 @@
|
||||
use crate::{path_element, Node, NodeRef};
|
||||
use alloc::rc::Rc;
|
||||
use error::Errno;
|
||||
|
||||
pub struct Ioctx {
|
||||
pub root: NodeRef,
|
||||
pub cwd: NodeRef,
|
||||
}
|
||||
|
||||
impl Ioctx {
|
||||
fn lookup_or_load(parent: NodeRef, name: &[u8]) -> Result<NodeRef, Errno> {
|
||||
let p = parent.borrow_mut();
|
||||
|
||||
if !p.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
|
||||
if let Some(node) = p.children().find(|&node| node.borrow().name() == name) {
|
||||
return Ok(node.clone());
|
||||
}
|
||||
|
||||
if let Some(ops) = p.ops.as_ref() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
fn _find(&self, mut at: NodeRef, path: &[u8]) -> Result<NodeRef, Errno> {
|
||||
let mut child_path: &[u8];
|
||||
let mut element_name: &[u8];
|
||||
|
||||
if path.is_empty() {
|
||||
return Ok(at);
|
||||
}
|
||||
|
||||
child_path = path;
|
||||
loop {
|
||||
let r = path_element(child_path);
|
||||
element_name = r.0;
|
||||
child_path = r.1;
|
||||
|
||||
match element_name {
|
||||
b"." => {
|
||||
if child_path.is_empty() {
|
||||
return Ok(at);
|
||||
}
|
||||
}
|
||||
b".." => {
|
||||
let parent = at.borrow().parent().unwrap_or(at.clone());
|
||||
at = parent;
|
||||
if child_path.is_empty() {
|
||||
return Ok(at);
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
if element_name.is_empty() && child_path.is_empty() {
|
||||
return Ok(at);
|
||||
}
|
||||
assert!(!element_name.is_empty());
|
||||
let child = Self::lookup_or_load(at, element_name)?;
|
||||
|
||||
if child_path.is_empty() {
|
||||
Ok(child)
|
||||
} else {
|
||||
self._find(child, child_path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find(&self, at: Option<NodeRef>, mut path: &[u8]) -> Result<NodeRef, Errno> {
|
||||
if path.is_empty() {
|
||||
return at.ok_or(Errno::DoesNotExist);
|
||||
}
|
||||
|
||||
let at = if path[0] == b'/' {
|
||||
let index = path
|
||||
.iter()
|
||||
.position(|&x| x != b'/')
|
||||
.unwrap_or(path.len() - 1);
|
||||
path = &path[index..];
|
||||
self.root.clone()
|
||||
} else if let Some(node) = at {
|
||||
node
|
||||
} else {
|
||||
self.cwd.clone()
|
||||
};
|
||||
|
||||
self._find(at, path)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Node;
|
||||
|
||||
#[test]
|
||||
fn ioctx_find_at() {
|
||||
let r = Node::directory(b"");
|
||||
let d0 = Node::directory(b"dir0");
|
||||
let d1 = Node::directory(b"dir1");
|
||||
let d0d0 = Node::directory(b"dir0");
|
||||
let d0f0 = Node::file(b"file0");
|
||||
|
||||
Node::attach(r.clone(), d0.clone());
|
||||
Node::attach(r.clone(), d1.clone());
|
||||
Node::attach(d0.clone(), d0d0.clone());
|
||||
Node::attach(d0.clone(), d0f0.clone());
|
||||
|
||||
let ioctx = Ioctx {
|
||||
root: r.clone(),
|
||||
cwd: r.clone(),
|
||||
};
|
||||
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(r.clone()), b"dir0").as_ref().unwrap(),
|
||||
&d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"dir0").as_ref().unwrap(),
|
||||
&d0d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(r.clone()), b"dir0/dir0").as_ref().unwrap(),
|
||||
&d0d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"file0").as_ref().unwrap(),
|
||||
&d0f0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(r.clone()), b"dir0/file0").as_ref().unwrap(),
|
||||
&d0f0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"../dir1").as_ref().unwrap(),
|
||||
&d1
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"./dir0").as_ref().unwrap(),
|
||||
&d0d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././dir0/.").as_ref().unwrap(),
|
||||
&d0d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././dir0/./").as_ref().unwrap(),
|
||||
&d0d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././dir0/").as_ref().unwrap(),
|
||||
&d0d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././dir0/..").as_ref().unwrap(),
|
||||
&d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././dir0/../..").as_ref().unwrap(),
|
||||
&r
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././dir0/../../../").as_ref().unwrap(),
|
||||
&r
|
||||
));
|
||||
|
||||
// TODO make these illegal
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././file0/..").as_ref().unwrap(),
|
||||
&d0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././file0/").as_ref().unwrap(),
|
||||
&d0f0
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
ioctx.find(Some(d0.clone()), b"././file0/.").as_ref().unwrap(),
|
||||
&d0f0
|
||||
));
|
||||
|
||||
ioctx.find(Some(r.clone()), b"dir0/dir1").expect_err("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
use error::Errno;
|
||||
|
||||
pub mod node;
|
||||
mod util;
|
||||
pub use node::{Node, NodeOperations, NodeRef, NodeType};
|
||||
mod ioctx;
|
||||
pub use ioctx::Ioctx;
|
||||
|
||||
pub fn path_element(path: &[u8]) -> (&[u8], &[u8]) {
|
||||
if let Some(mut index) = path.iter().position(|&x| x == b'/') {
|
||||
let elem = &path[..index];
|
||||
while index < path.len() && path[index] == b'/' {
|
||||
index += 1;
|
||||
}
|
||||
(elem, &path[index..])
|
||||
} else {
|
||||
(path, b"")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
use crate::util::{iter::LockedIterator, FixedStr};
|
||||
use alloc::{boxed::Box, rc::Rc, vec::Vec};
|
||||
use core::any::Any;
|
||||
use core::cell::RefCell;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
use core::ptr::null_mut;
|
||||
use error::Errno;
|
||||
use spin::Mutex;
|
||||
|
||||
pub type NodeRef = Rc<RefCell<Node>>;
|
||||
|
||||
pub enum NodeType {
|
||||
Regular,
|
||||
Directory { children: Vec<NodeRef> },
|
||||
}
|
||||
|
||||
pub const NODE_MEMORY: u32 = 1 << 0;
|
||||
|
||||
pub struct NodeOperations {
|
||||
lookup: Option<fn(parent: NodeRef, name: &[u8]) -> Result<NodeRef, Errno>>,
|
||||
drop: Option<fn(node: &mut Node)>,
|
||||
}
|
||||
|
||||
pub struct Node {
|
||||
name: FixedStr<64>,
|
||||
typ: NodeType,
|
||||
flags: u32,
|
||||
|
||||
pub(crate) ops: Option<&'static NodeOperations>,
|
||||
pub data: *mut c_void,
|
||||
|
||||
target: Option<NodeRef>,
|
||||
parent: Option<NodeRef>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new(name: &[u8], typ: NodeType) -> Self {
|
||||
let mut r = Self {
|
||||
name: FixedStr::empty(),
|
||||
typ,
|
||||
flags: 0,
|
||||
|
||||
ops: None,
|
||||
data: null_mut(),
|
||||
|
||||
parent: None,
|
||||
target: None,
|
||||
};
|
||||
r.name.copy_from_slice(name);
|
||||
r
|
||||
}
|
||||
|
||||
pub fn directory(name: &[u8]) -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self::new(
|
||||
name,
|
||||
NodeType::Directory { children: vec![] },
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn file(name: &[u8]) -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self::new(name, NodeType::Regular)))
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &FixedStr<64> {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<NodeRef> {
|
||||
self.parent.clone()
|
||||
}
|
||||
|
||||
pub fn is_directory(&self) -> bool {
|
||||
matches!(&self.typ, NodeType::Directory { .. })
|
||||
}
|
||||
|
||||
pub fn children(&self) -> impl Iterator<Item = &NodeRef> {
|
||||
let lock = TREE_MUTEX.lock();
|
||||
match &self.typ {
|
||||
NodeType::Directory { children } => LockedIterator::new(children.iter(), lock),
|
||||
_ => panic!("Not a directory"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn children_mut(&mut self) -> impl Iterator<Item = &mut NodeRef> {
|
||||
let lock = TREE_MUTEX.lock();
|
||||
match &mut self.typ {
|
||||
NodeType::Directory { children } => LockedIterator::new(children.iter_mut(), lock),
|
||||
_ => panic!("Not a directory"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach(parent: NodeRef, child: NodeRef) {
|
||||
let _lock = TREE_MUTEX.lock();
|
||||
assert!(child.borrow().parent.is_none());
|
||||
match &mut parent.borrow_mut().typ {
|
||||
NodeType::Directory { children } => children.push(child.clone()),
|
||||
_ => panic!("Not a directory"),
|
||||
}
|
||||
child.borrow_mut().parent.replace(parent.clone());
|
||||
}
|
||||
|
||||
pub fn detach(child: NodeRef) {
|
||||
let _lock = TREE_MUTEX.lock();
|
||||
assert!(child.borrow().parent.is_some());
|
||||
let parent = child.borrow_mut().parent.take().unwrap();
|
||||
match &mut parent.borrow_mut().typ {
|
||||
NodeType::Directory { children } => {
|
||||
children.remove(children.iter().position(|x| Rc::ptr_eq(x, &child)).unwrap());
|
||||
}
|
||||
_ => panic!("Not a directory"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Node {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ops) = self.ops.as_ref() {
|
||||
if let Some(do_drop) = ops.drop.as_ref() {
|
||||
do_drop(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Node {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Node {{ name={:?}, typ=??? }}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
static TREE_MUTEX: Mutex<()> = Mutex::new(());
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core::any::{type_name, TypeId};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[test]
|
||||
fn node_new() {
|
||||
let r = Node::directory(b"");
|
||||
let n = r.borrow();
|
||||
assert_eq!(n.name, b""[..]);
|
||||
assert!(matches!(n.typ, NodeType::Directory { .. }));
|
||||
assert!(n.ops.is_none());
|
||||
assert!(n.target.is_none());
|
||||
assert!(n.parent.is_none());
|
||||
|
||||
let r = Node::file(b"file1");
|
||||
let n = r.borrow();
|
||||
assert_eq!(n.name, b"file1"[..]);
|
||||
assert!(matches!(n.typ, NodeType::Regular));
|
||||
assert!(n.ops.is_none());
|
||||
assert!(n.target.is_none());
|
||||
assert!(n.parent.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn node_attach() {
|
||||
let r0 = Node::directory(b"");
|
||||
let r1 = Node::directory(b"1234");
|
||||
|
||||
// Attach
|
||||
Node::attach(r0.clone(), r1.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(r1.borrow().parent.as_ref().unwrap(), &r0));
|
||||
{
|
||||
let n0 = r0.borrow();
|
||||
let mut it = n0.children();
|
||||
assert!(Rc::ptr_eq(&it.next().unwrap(), &r1));
|
||||
assert!(it.next().is_none());
|
||||
}
|
||||
|
||||
// Detach
|
||||
Node::detach(r1.clone());
|
||||
|
||||
assert!(r1.borrow().parent.is_none());
|
||||
assert_eq!(r0.borrow().children().count(), 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
use spin::MutexGuard;
|
||||
|
||||
pub struct LockedIterator<'a, T: ?Sized, I: Iterator<Item = T>> {
|
||||
inner: I,
|
||||
#[allow(dead_code)]
|
||||
lock: MutexGuard<'a, ()>,
|
||||
}
|
||||
|
||||
impl<'a, T, I: Iterator<Item = T>> Iterator for LockedIterator<'a, T, I> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, I: Iterator<Item = T>> LockedIterator<'a, T, I> {
|
||||
#[inline]
|
||||
pub fn new(inner: I, lock: MutexGuard<'a, ()>) -> Self {
|
||||
Self { inner, lock }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use alloc::boxed::Box;
|
||||
use core::alloc::Layout;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
|
||||
pub mod iter;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct FixedStr<const N: usize> {
|
||||
inner: [u8; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> FixedStr<N> {
|
||||
pub fn new(s: &[u8]) -> Self {
|
||||
let mut r = Self::empty();
|
||||
r.copy_from_slice(s);
|
||||
r
|
||||
}
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
Self { inner: [0; N] }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.iter().position(|&n| n == 0).unwrap_or(N)
|
||||
}
|
||||
|
||||
pub fn copy_from_slice(&mut self, src: &[u8]) {
|
||||
let src_len = src.len();
|
||||
if src_len == 0 {
|
||||
self.inner[0] = 0;
|
||||
return;
|
||||
}
|
||||
if src_len >= N {
|
||||
panic!("String buffer overflow");
|
||||
}
|
||||
let dst = &mut self.inner[..src_len];
|
||||
dst.copy_from_slice(src);
|
||||
self.inner[src_len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<[u8]> for FixedStr<N> {
|
||||
fn eq(&self, other: &[u8]) -> bool {
|
||||
let self_len = self.len();
|
||||
if self_len != other.len() {
|
||||
return false;
|
||||
}
|
||||
&self.inner[..self_len] == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Display for FixedStr<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for byte in self.inner {
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
write!(f, "{}", byte as char)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for FixedStr<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fixed_str_display() {
|
||||
let s = FixedStr::<64>::new(b"test");
|
||||
assert_eq!(format!("{}", s), "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_str_length() {
|
||||
assert_eq!(FixedStr::<64>::empty().len(), 0);
|
||||
assert_eq!(FixedStr::<64>::new(b"test1").len(), 5);
|
||||
assert_eq!(FixedStr::<6>::new(b"test1").len(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_str_eq_slice() {
|
||||
assert_eq!(FixedStr::<64>::empty(), b""[..]);
|
||||
assert_eq!(FixedStr::<64>::new(b"1234"), b"1234"[..]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user