Add DirEntryVec and use it to refine ramfs.

This commit is contained in:
LI Qing 2023-03-24 06:15:08 -04:00 committed by Tate, Hongliang Tian
parent d0bcd2491f
commit 12b4e0637d
3 changed files with 111 additions and 16 deletions

View File

@ -9,7 +9,8 @@ use spin::{RwLock, RwLockWriteGuard};
use super::*;
use crate::fs::utils::{
DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata, SuperBlock,
DirEntryVec, DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd,
Metadata, SuperBlock,
};
pub struct RamFS {
@ -159,7 +160,7 @@ impl Inner {
}
struct DirEntry {
children: LinkedList<(Str256, Arc<RamInode>)>,
children: DirEntryVec<(Str256, Arc<RamInode>)>,
this: Weak<RamInode>,
parent: Weak<RamInode>,
}
@ -167,7 +168,7 @@ struct DirEntry {
impl DirEntry {
fn new() -> Self {
Self {
children: LinkedList::new(),
children: DirEntryVec::new(),
this: Weak::default(),
parent: Weak::default(),
}
@ -200,26 +201,28 @@ impl DirEntry {
Some((1, self.parent.upgrade().unwrap()))
} else {
self.children
.iter()
.enumerate()
.find(|(idx, (child, inode))| child == &Str256::from(name))
.idxes_and_entries()
.find(|(_, (child, _))| child == &Str256::from(name))
.map(|(idx, (_, inode))| (idx + 2, inode.clone()))
}
}
fn append_entry(&mut self, name: &str, inode: Arc<RamInode>) {
self.children.push_back((Str256::from(name), inode))
self.children.put((Str256::from(name), inode))
}
fn remove_entry(&mut self, idx: usize) -> (Str256, Arc<RamInode>) {
fn remove_entry(&mut self, idx: usize) -> Option<(Str256, Arc<RamInode>)> {
assert!(idx >= 2);
self.children.remove(idx - 2)
}
fn modify_entry(&mut self, idx: usize, new_name: &str) {
fn substitute_entry(
&mut self,
idx: usize,
new_entry: (Str256, Arc<RamInode>),
) -> Option<(Str256, Arc<RamInode>)> {
assert!(idx >= 2);
let (name, _) = self.children.iter_mut().nth(idx - 2).unwrap();
*name = Str256::from(new_name);
self.children.put_at(idx - 2, new_entry)
}
fn visit_entry(&self, mut idx: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
@ -246,14 +249,21 @@ impl DirEntry {
*idx += 1;
}
// Read the normal child entries.
for (name, child) in self.children.iter().skip(*idx - 2) {
for (offset, (name, child)) in self
.children
.idxes_and_entries()
.map(|(offset, (name, child))| (offset + 2, (name, child)))
{
if offset < *idx {
continue;
}
visitor.visit(
name.as_ref(),
child.metadata().ino as u64,
child.metadata().type_,
*idx,
offset,
)?;
*idx += 1;
*idx = offset + 1;
}
Ok(())
};
@ -555,10 +565,10 @@ impl Inode for RamInode {
if self.metadata().ino == target.metadata().ino {
let mut self_inode = self.0.write();
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (idx, _) = self_dir
let (idx, inode) = self_dir
.get_entry(old_name)
.ok_or(Error::new(Errno::ENOENT))?;
self_dir.modify_entry(idx, new_name);
self_dir.substitute_entry(idx, (Str256::from(new_name), inode));
} else {
let (mut self_inode, mut target_inode) = write_lock_two_inodes(self, target);
let self_dir = self_inode.inner.as_direntry_mut().unwrap();

View File

@ -0,0 +1,83 @@
use crate::prelude::*;
/// DirEntryVec is used to store the entries of a directory.
/// It can guarantee that the index of one dir entry remains unchanged during
/// adding or deleting other dir entries of it.
pub struct DirEntryVec<T> {
// The slots to store dir entries.
slots: Vec<Option<T>>,
// The number of occupied slots.
// The i-th slot is occupied if `self.slots[i].is_some()`.
num_occupied: usize,
}
impl<T> DirEntryVec<T> {
/// New an empty vec.
pub fn new() -> Self {
Self {
slots: Vec::new(),
num_occupied: 0,
}
}
/// Returns `true` if the vec contains no entries.
pub fn is_empty(&self) -> bool {
self.num_occupied == 0
}
/// Put a dir entry into the vec.
/// it may be put into an existing empty slot or the back of the vec.
pub fn put(&mut self, entry: T) {
if self.num_occupied == self.slots.len() {
self.slots.push(Some(entry));
} else {
let idx = self.slots.iter().position(|x| x.is_none()).unwrap();
self.slots[idx] = Some(entry);
}
self.num_occupied += 1;
}
/// Removes and returns the entry at position `idx`.
/// Returns `None` if `idx` is out of bounds or the entry has been removed.
pub fn remove(&mut self, idx: usize) -> Option<T> {
if idx >= self.slots.len() {
return None;
}
let mut del_entry = None;
core::mem::swap(&mut del_entry, &mut self.slots[idx]);
if del_entry.is_some() {
debug_assert!(self.num_occupied > 0);
self.num_occupied -= 1;
}
del_entry
}
/// Put and returns the entry at position `idx`.
/// Returns `None` if `idx` is out of bounds or the entry has been removed.
pub fn put_at(&mut self, idx: usize, entry: T) -> Option<T> {
if idx >= self.slots.len() {
return None;
}
let mut sub_entry = Some(entry);
core::mem::swap(&mut sub_entry, &mut self.slots[idx]);
if sub_entry.is_none() {
self.num_occupied += 1;
}
sub_entry
}
/// Creates an iterator which gives both of the index and the dir entry.
/// The index may not be continuous.
pub fn idxes_and_entries(&self) -> impl Iterator<Item = (usize, &'_ T)> {
self.slots
.iter()
.enumerate()
.filter(|(_, x)| x.is_some())
.map(|(idx, x)| (idx, x.as_ref().unwrap()))
}
/// Creates an iterator which gives the dir entry.
pub fn iter(&self) -> impl Iterator<Item = &'_ T> {
self.slots.iter().filter_map(|x| x.as_ref())
}
}

View File

@ -4,6 +4,7 @@ pub use access_mode::AccessMode;
pub use creation_flags::CreationFlags;
pub use dentry_cache::Dentry;
pub use dirent_visitor::DirentVisitor;
pub use direntry_vec::DirEntryVec;
pub use fcntl::FcntlCmd;
pub use file_creation_mask::FileCreationMask;
pub use fs::{FileSystem, FsFlags, SuperBlock};
@ -19,6 +20,7 @@ mod access_mode;
mod creation_flags;
mod dentry_cache;
mod dirent_visitor;
mod direntry_vec;
mod fcntl;
mod file_creation_mask;
mod fs;