|
|
|
@ -1,79 +1,7 @@
|
|
|
|
|
use super::*;
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::borrow::Borrow;
|
|
|
|
|
use std::iter::FusedIterator;
|
|
|
|
|
use std::cell::RefCell;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
/// A reference to an INode within a graph.
|
|
|
|
|
pub struct INodeRef<'a>(&'a INodeInfoGraph, INode);
|
|
|
|
|
|
|
|
|
|
impl<'a> AsRef<Path> for INodeRef<'a>
|
|
|
|
|
{
|
|
|
|
|
fn as_ref(&self) -> &Path
|
|
|
|
|
{
|
|
|
|
|
self.0.paths_reverse.get(&self.1).unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Borrow<INode> for INodeRef<'a>
|
|
|
|
|
{
|
|
|
|
|
fn borrow(&self) -> &INode
|
|
|
|
|
{
|
|
|
|
|
&self.1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> INodeRef<'a>
|
|
|
|
|
{
|
|
|
|
|
/// The owning graph of this INode reference.
|
|
|
|
|
#[inline] pub fn graph(&self) -> &INodeInfoGraph
|
|
|
|
|
{
|
|
|
|
|
self.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The `FsInfo` for this INode
|
|
|
|
|
#[inline] pub fn info(&self) -> &FsInfo
|
|
|
|
|
{
|
|
|
|
|
self.0.inodes.get(&self.1).unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Compute the total size of this INode.
|
|
|
|
|
///
|
|
|
|
|
/// If this is a file, it is O(1). Otherwise, recursively compute the sizes of all children.
|
|
|
|
|
#[inline] pub fn size(&self) -> u64
|
|
|
|
|
{
|
|
|
|
|
self.0.lookup_size_or(&self.1, move || {
|
|
|
|
|
match self.info()
|
|
|
|
|
{
|
|
|
|
|
FsInfo::File(sz, _) => *sz,
|
|
|
|
|
FsInfo::Directory(_) => {
|
|
|
|
|
self.children().map(|x| x.size()).sum()
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The internal INode
|
|
|
|
|
#[inline] pub fn inode(&self) -> INode
|
|
|
|
|
{
|
|
|
|
|
self.1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The path this INode refers to
|
|
|
|
|
#[inline] pub fn path(&self) -> &Path
|
|
|
|
|
{
|
|
|
|
|
self.as_ref()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get an iterator over the children of this INode, if it has children.
|
|
|
|
|
#[inline] pub fn children(&self) -> Children<'a>
|
|
|
|
|
{
|
|
|
|
|
self.0.children_of(&self.1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Contains a graph of all paths and inodes that were successfully stat'd
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
@ -85,43 +13,16 @@ pub struct INodeInfoGraph
|
|
|
|
|
paths_reverse: HashMap<INode, PathBuf>, // reverse lookup of INode to PathBuf,
|
|
|
|
|
|
|
|
|
|
children: HashMap<INode, Vec<INode>>, //reverse lookup for directory INodes and their parent INodes
|
|
|
|
|
|
|
|
|
|
total_sizes_cached: RefCell<HashMap<INode, u64>>, // the total sizes of all INodes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl INodeInfoGraph
|
|
|
|
|
{
|
|
|
|
|
fn lookup_insert_size<F>(&self, node: INode, with: F) -> u64
|
|
|
|
|
where F: FnOnce() -> u64
|
|
|
|
|
{
|
|
|
|
|
let w = with();
|
|
|
|
|
|
|
|
|
|
if let Ok(mut cache) = self.total_sizes_cached.try_borrow_mut()
|
|
|
|
|
{
|
|
|
|
|
cache.insert(node, w);
|
|
|
|
|
w
|
|
|
|
|
} else {
|
|
|
|
|
w
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fn lookup_size_or<F>(&self, node: impl Borrow<INode>, or: F) -> u64
|
|
|
|
|
where F: FnOnce() -> u64
|
|
|
|
|
{
|
|
|
|
|
if let Ok(cache) = self.total_sizes_cached.try_borrow() {
|
|
|
|
|
let node = node.borrow();
|
|
|
|
|
if let Some(sz) = cache.get(node) {
|
|
|
|
|
return *sz;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
self.lookup_insert_size(*node.borrow(), or)
|
|
|
|
|
}
|
|
|
|
|
/// Create a new graph from these linked `HashMap`s
|
|
|
|
|
#[inline] pub fn new(inodes: HashMap<INode, FsInfo>, paths: HashMap<PathBuf, INode>) -> Self
|
|
|
|
|
{
|
|
|
|
|
Self {
|
|
|
|
|
children: HashMap::with_capacity(inodes.len()),
|
|
|
|
|
paths_reverse: HashMap::with_capacity(paths.len()),
|
|
|
|
|
total_sizes_cached: RefCell::new(HashMap::with_capacity(inodes.len())),
|
|
|
|
|
inodes,
|
|
|
|
|
paths,
|
|
|
|
|
}
|
|
|
|
@ -153,58 +54,194 @@ impl INodeInfoGraph
|
|
|
|
|
self.inodes.get(node.borrow())
|
|
|
|
|
}
|
|
|
|
|
/// An iterator over top-level children of this node
|
|
|
|
|
pub fn children_of(&self, node: impl Borrow<INode>) -> Children<'_>
|
|
|
|
|
pub fn children_of(&self, node: impl Borrow<INode>) -> !//Children<'_>
|
|
|
|
|
{
|
|
|
|
|
Children(self, match self.children.get(node.borrow()) {
|
|
|
|
|
Some(slc) => slc.iter(),
|
|
|
|
|
_ => [].iter(),
|
|
|
|
|
})
|
|
|
|
|
todo!();
|
|
|
|
|
/*Children(self, match self.children.get(node.borrow()) {
|
|
|
|
|
Some(slc) => slc.iter(),
|
|
|
|
|
_ => [].iter(),
|
|
|
|
|
})*/
|
|
|
|
|
}
|
|
|
|
|
/// An iterator over all the directories in this
|
|
|
|
|
pub fn directories(&self) -> Directories<'_>
|
|
|
|
|
pub fn directories(&self) -> !//Directories<'_>
|
|
|
|
|
{
|
|
|
|
|
Directories(self, self.children.keys())
|
|
|
|
|
todo!()//Directories(self, self.children.keys())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// An iterator over all directories in a graph
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct Directories<'a>(&'a INodeInfoGraph, std::collections::hash_map::Keys<'a, INode, Vec<INode>>);
|
|
|
|
|
|
|
|
|
|
impl<'a> Iterator for Directories<'a>
|
|
|
|
|
{
|
|
|
|
|
type Item = INodeRef<'a>;
|
|
|
|
|
#[inline] fn next(&mut self) -> Option<Self::Item>
|
|
|
|
|
/// Convert into a hierarchical representation
|
|
|
|
|
pub fn into_hierarchical(self) -> HierarchicalINodeGraph
|
|
|
|
|
{
|
|
|
|
|
self.1.next().map(|x| INodeRef(self.0, *x))
|
|
|
|
|
HierarchicalINodeGraph::create(self)
|
|
|
|
|
}
|
|
|
|
|
#[inline] fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
|
self.1.size_hint()
|
|
|
|
|
|
|
|
|
|
/// Convert into a hierarchical representation
|
|
|
|
|
pub fn create_hierarchical(&self) -> HierarchicalINodeGraph
|
|
|
|
|
{
|
|
|
|
|
HierarchicalINodeGraph::create_from(self)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl<'a> ExactSizeIterator for Directories<'a>{}
|
|
|
|
|
impl<'a> FusedIterator for Directories<'a>{}
|
|
|
|
|
|
|
|
|
|
/// The immediate children of a specific INode
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct Children<'a>(&'a INodeInfoGraph, std::slice::Iter<'a, INode>);
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
enum NodeKind
|
|
|
|
|
{
|
|
|
|
|
Directory(Vec<PathBuf>, Option<u64>),
|
|
|
|
|
File(u64),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Iterator for Children<'a>
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
struct HierarchicalNode
|
|
|
|
|
{
|
|
|
|
|
kind: NodeKind,
|
|
|
|
|
inode: INode,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A hierarchical graph of node sizes
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct HierarchicalINodeGraph
|
|
|
|
|
{
|
|
|
|
|
type Item = INodeRef<'a>;
|
|
|
|
|
#[inline] fn next(&mut self) -> Option<Self::Item>
|
|
|
|
|
table: HashMap<PathBuf, HierarchicalNode>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl HierarchicalINodeGraph
|
|
|
|
|
{
|
|
|
|
|
/// Compute the sizes of directories in the graph
|
|
|
|
|
pub fn compute_recursive_sizes(&mut self)
|
|
|
|
|
{
|
|
|
|
|
self.1.next().map(|x| INodeRef(self.0, *x))
|
|
|
|
|
fn compute_for_dir(this: &HashMap<PathBuf, HierarchicalNode>, children: &Vec<PathBuf>) -> u64
|
|
|
|
|
{
|
|
|
|
|
let mut total = 0;
|
|
|
|
|
for child in children.iter()
|
|
|
|
|
{
|
|
|
|
|
let chl = if let Some(ch) = this.get(child)
|
|
|
|
|
{
|
|
|
|
|
match ch {
|
|
|
|
|
HierarchicalNode { kind: NodeKind::Directory(_, Some(sz)), .. } => {
|
|
|
|
|
total += sz;
|
|
|
|
|
continue;
|
|
|
|
|
},
|
|
|
|
|
HierarchicalNode { kind: NodeKind::Directory(children, _), .. } => {
|
|
|
|
|
children
|
|
|
|
|
},
|
|
|
|
|
HierarchicalNode { kind: NodeKind::File(sz), .. } => {
|
|
|
|
|
total += sz;
|
|
|
|
|
continue;
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
continue;
|
|
|
|
|
};
|
|
|
|
|
total += compute_for_dir(this, chl);
|
|
|
|
|
}
|
|
|
|
|
total
|
|
|
|
|
}
|
|
|
|
|
let mut length_proxy = HashMap::with_capacity(self.table.len());
|
|
|
|
|
|
|
|
|
|
for (path, hnode) in self.table.iter()
|
|
|
|
|
{
|
|
|
|
|
let (children, len) = match hnode {
|
|
|
|
|
HierarchicalNode { kind: NodeKind::Directory(children, len), .. } => {
|
|
|
|
|
(children, len)
|
|
|
|
|
},
|
|
|
|
|
_ => continue,
|
|
|
|
|
};
|
|
|
|
|
match len {
|
|
|
|
|
Some(sz) => length_proxy.insert(path.clone(), *sz),
|
|
|
|
|
None => length_proxy.insert(path.clone(), compute_for_dir(&self.table, children)),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (pathref, sz) in length_proxy.into_iter()
|
|
|
|
|
{
|
|
|
|
|
self.table.get_mut(&pathref).map(|hnode| {
|
|
|
|
|
match hnode {
|
|
|
|
|
HierarchicalNode { kind: NodeKind::Directory(_, ss), .. } => {
|
|
|
|
|
*ss = Some(sz);
|
|
|
|
|
},
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
|
self.1.size_hint()
|
|
|
|
|
fn create_from(graph: &INodeInfoGraph) -> Self
|
|
|
|
|
{
|
|
|
|
|
let mut new = Self {
|
|
|
|
|
table: HashMap::with_capacity(graph.inodes.len()),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (path, &inode) in graph.paths.iter()
|
|
|
|
|
{
|
|
|
|
|
//Lookup the INode in graph inode table
|
|
|
|
|
if let Some(fsinfo) = graph.inodes.get(&inode)
|
|
|
|
|
{
|
|
|
|
|
match fsinfo {
|
|
|
|
|
FsInfo::File(sz, parent) => {
|
|
|
|
|
new.table.insert(path.clone(), HierarchicalNode {
|
|
|
|
|
kind: NodeKind::File(*sz),
|
|
|
|
|
inode,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
FsInfo::Directory(parent) => {
|
|
|
|
|
new.table.insert(path.clone(), HierarchicalNode {
|
|
|
|
|
kind: NodeKind::Directory(graph.children.get(&inode).unwrap().iter().map(|node| {
|
|
|
|
|
graph.paths_reverse.get(node).unwrap().clone()
|
|
|
|
|
}).collect(), None),
|
|
|
|
|
inode,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new
|
|
|
|
|
}
|
|
|
|
|
fn create(graph: INodeInfoGraph) -> Self
|
|
|
|
|
{
|
|
|
|
|
let mut new = Self {
|
|
|
|
|
table: HashMap::with_capacity(graph.inodes.len()),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
macro_rules! unwrap {
|
|
|
|
|
($expr:expr) => {
|
|
|
|
|
match $expr {
|
|
|
|
|
Some(ex) => ex,
|
|
|
|
|
_ => continue,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (path, &inode) in graph.paths.iter()
|
|
|
|
|
{
|
|
|
|
|
let path = path.clone();
|
|
|
|
|
//Lookup the INode in graph inode table
|
|
|
|
|
if let Some(fsinfo) = graph.inodes.get(&inode)
|
|
|
|
|
{
|
|
|
|
|
match fsinfo {
|
|
|
|
|
FsInfo::File(sz, _) => {
|
|
|
|
|
new.table.insert(path, HierarchicalNode {
|
|
|
|
|
kind: NodeKind::File(*sz),
|
|
|
|
|
inode,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
FsInfo::Directory(_) => {
|
|
|
|
|
new.table.insert(path, HierarchicalNode {
|
|
|
|
|
kind: NodeKind::Directory(unwrap!(graph.children.get(&inode)).iter().map(|node| {
|
|
|
|
|
graph.paths_reverse.get(node).unwrap().clone()
|
|
|
|
|
}).collect(), None),
|
|
|
|
|
inode,
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl<'a> ExactSizeIterator for Children<'a>{}
|
|
|
|
|
impl<'a> FusedIterator for Children<'a>{}
|
|
|
|
|
impl<'a> DoubleEndedIterator for Children<'a>
|
|
|
|
|
|
|
|
|
|
impl From<INodeInfoGraph> for HierarchicalINodeGraph
|
|
|
|
|
{
|
|
|
|
|
#[inline] fn next_back(&mut self) -> Option<Self::Item> {
|
|
|
|
|
self.1.next_back().map(|x| INodeRef(self.0, *x))
|
|
|
|
|
fn from(from: INodeInfoGraph) -> Self
|
|
|
|
|
{
|
|
|
|
|
Self::create(from)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|