Add DirEntryVec and use it to refine ramfs.
This commit is contained in:
parent
d0bcd2491f
commit
12b4e0637d
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue