Skip to content

Commit 83dea11

Browse files
committed
WIP: rename
1 parent af7622e commit 83dea11

1 file changed

Lines changed: 78 additions & 8 deletions

File tree

src/inode_table.rs

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
//
55

66
use std::borrow::Borrow;
7-
use std::cmp::{Eq, PartialEq};
8-
use std::collections::{HashMap, VecDeque};
9-
use std::collections::hash_map::Entry::*;
7+
use std::cmp::{Eq, Ordering, PartialEq};
8+
use std::collections::{BTreeMap, VecDeque};
9+
use std::collections::btree_map::Entry::*;
1010
use std::hash::{Hash, Hasher};
11+
use std::ops::Bound::{Excluded, Included, Unbounded};
1112
use std::path::{Path, PathBuf};
1213
use std::sync::Arc;
1314

@@ -27,7 +28,7 @@ struct InodeTableEntry {
2728
pub struct InodeTable {
2829
table: Vec<InodeTableEntry>,
2930
free_list: VecDeque<usize>,
30-
by_path: HashMap<Arc<PathBuf>, usize>,
31+
by_path: BTreeMap<Arc<PathBuf>, usize>,
3132
}
3233

3334
impl InodeTable {
@@ -42,7 +43,7 @@ impl InodeTable {
4243
let mut inode_table = InodeTable {
4344
table: Vec::new(),
4445
free_list: VecDeque::new(),
45-
by_path: HashMap::new()
46+
by_path: BTreeMap::new()
4647
};
4748
let root = Arc::new(PathBuf::from("/"));
4849
inode_table.table.push(InodeTableEntry {
@@ -180,9 +181,38 @@ impl InodeTable {
180181
/// Change an inode's path to a different one, without changing the inode number.
181182
/// Lookup counts remain unchanged, even if this is replacing another file.
182183
pub fn rename(&mut self, oldpath: &Path, newpath: Arc<PathBuf>) {
183-
let idx = self.by_path.remove(Pathish::new(oldpath)).unwrap();
184-
self.table[idx].path = Some(newpath.clone());
185-
self.by_path.insert(newpath, idx); // this can replace a path with a new inode
184+
// none means it's a single path
185+
let mut range = None;
186+
for (candidate, _) in self.by_path.range::<Pathish, _>((Excluded(Pathish::new(oldpath)), Unbounded)) {
187+
if candidate.starts_with(oldpath) {
188+
if let Some((start, _end)) = range {
189+
range = Some((start, Included(candidate.clone())));
190+
} else {
191+
range = Some((Included(Arc::new(oldpath.to_owned())), Included(Arc::clone(candidate))));
192+
}
193+
} else {
194+
break;
195+
}
196+
}
197+
198+
if let Some(range) = range {
199+
let mut new_entries = vec![];
200+
for (path, idx) in self.by_path.extract_if(range, |_, _| true) {
201+
let suffix = path.strip_prefix(oldpath).unwrap();
202+
let new_entry_path = if suffix.as_os_str().is_empty() {
203+
Arc::clone(&newpath)
204+
} else {
205+
Arc::new(newpath.as_path().join(suffix))
206+
};
207+
self.table[idx].path = Some(Arc::clone(&new_entry_path));
208+
new_entries.push((new_entry_path, idx));
209+
}
210+
self.by_path.extend(new_entries);
211+
} else {
212+
let idx = self.by_path.remove(Pathish::new(oldpath)).unwrap();
213+
self.table[idx].path = Some(newpath.clone());
214+
self.by_path.insert(newpath, idx); // this can replace a path with a new inode
215+
}
186216
}
187217

188218
/// Remove the path->inode mapping for a given path, but keep the inode around.
@@ -245,6 +275,18 @@ impl Hash for Pathish {
245275
impl Eq for Pathish {
246276
}
247277

278+
impl PartialOrd<Self> for Pathish {
279+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
280+
Some(self.cmp(other))
281+
}
282+
}
283+
284+
impl Ord for Pathish {
285+
fn cmp(&self, other: &Self) -> Ordering {
286+
self.inner.cmp(&other.inner)
287+
}
288+
}
289+
248290
impl PartialEq for Pathish {
249291
fn eq(&self, other: &Pathish) -> bool {
250292
self.inner.eq(&other.inner)
@@ -341,3 +383,31 @@ fn test_unlink() {
341383
assert_eq!(0, table.forget(inode, 1));
342384
assert!(table.get_path(inode).is_none());
343385
}
386+
387+
#[test]
388+
fn test_rename_directory() {
389+
let mut table = InodeTable::new();
390+
let a = table.add(Arc::new(PathBuf::from("/a_file")));
391+
let d = table.add(Arc::new(PathBuf::from("/directory")));
392+
let d_f1 = table.add(Arc::new(PathBuf::from("/directory/file1")));
393+
let d_f2 = table.add(Arc::new(PathBuf::from("/directory/file2")));
394+
let z = table.add(Arc::new(PathBuf::from("/z_file")));
395+
396+
table.rename(Path::new("/a_file"), Arc::new(PathBuf::from("/a_file_renamed")));
397+
assert_eq!(table.get_inode(Path::new("/a_file")), None);
398+
assert_eq!(table.get_inode(Path::new("/a_file_renamed")), Some(a.0));
399+
400+
table.rename(Path::new("/directory"), Arc::new(PathBuf::from("/new_directory")));
401+
402+
assert_eq!(table.get_inode(Path::new("/a_file_renamed")), Some(a.0));
403+
assert_eq!(table.get_inode(Path::new("/z_file")), Some(z.0));
404+
405+
assert_eq!(table.get_inode(Path::new("/new_directory")), Some(d.0));
406+
assert_eq!(table.get_inode(Path::new("/new_directory/file1")), Some(d_f1.0));
407+
assert_eq!(table.get_inode(Path::new("/new_directory/file2")), Some(d_f2.0));
408+
409+
assert_eq!(table.get_inode(Path::new("/directory")), None);
410+
assert_eq!(table.get_inode(Path::new("/directory/file1")), None);
411+
assert_eq!(table.get_inode(Path::new("/directory/file2")), None);
412+
413+
}

0 commit comments

Comments
 (0)