redo-gragh
Avril 4 years ago
parent 55f611b1af
commit 0f263fd5ed
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,79 +1,7 @@
use super::*; use super::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use std::borrow::Borrow; 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 /// Contains a graph of all paths and inodes that were successfully stat'd
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -85,43 +13,16 @@ pub struct INodeInfoGraph
paths_reverse: HashMap<INode, PathBuf>, // reverse lookup of INode to PathBuf, 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 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 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 /// Create a new graph from these linked `HashMap`s
#[inline] pub fn new(inodes: HashMap<INode, FsInfo>, paths: HashMap<PathBuf, INode>) -> Self #[inline] pub fn new(inodes: HashMap<INode, FsInfo>, paths: HashMap<PathBuf, INode>) -> Self
{ {
Self { Self {
children: HashMap::with_capacity(inodes.len()), children: HashMap::with_capacity(inodes.len()),
paths_reverse: HashMap::with_capacity(paths.len()), paths_reverse: HashMap::with_capacity(paths.len()),
total_sizes_cached: RefCell::new(HashMap::with_capacity(inodes.len())),
inodes, inodes,
paths, paths,
} }
@ -153,58 +54,194 @@ impl INodeInfoGraph
self.inodes.get(node.borrow()) self.inodes.get(node.borrow())
} }
/// An iterator over top-level children of this node /// 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()) { todo!();
Some(slc) => slc.iter(), /*Children(self, match self.children.get(node.borrow()) {
_ => [].iter(), Some(slc) => slc.iter(),
}) _ => [].iter(),
})*/
} }
/// An iterator over all the directories in this /// 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> /// Convert into a hierarchical representation
{ pub fn into_hierarchical(self) -> HierarchicalINodeGraph
type Item = INodeRef<'a>;
#[inline] fn next(&mut self) -> Option<Self::Item>
{ {
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, PartialEq, Eq)]
#[derive(Debug, Clone)] enum NodeKind
pub struct Children<'a>(&'a INodeInfoGraph, std::slice::Iter<'a, INode>); {
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>; table: HashMap<PathBuf, HierarchicalNode>
#[inline] fn next(&mut self) -> Option<Self::Item> }
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>) { fn create_from(graph: &INodeInfoGraph) -> Self
self.1.size_hint() {
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 From<INodeInfoGraph> for HierarchicalINodeGraph
impl<'a> DoubleEndedIterator for Children<'a>
{ {
#[inline] fn next_back(&mut self) -> Option<Self::Item> { fn from(from: INodeInfoGraph) -> Self
self.1.next_back().map(|x| INodeRef(self.0, *x)) {
Self::create(from)
} }
} }

@ -48,8 +48,13 @@ async fn main() -> eyre::Result<()> {
.with_suggestion(|| "Try running `--help`")?); .with_suggestion(|| "Try running `--help`")?);
let graph = work::work_on_all(state).await; let graph = work::work_on_all(state).await;
let max_size = graph.directories().map(|x| x.size()).max(); let mut graph = graph.into_hierarchical();
println!("Max size: {:?}", max_size); graph.compute_recursive_sizes();
println!("{:?}", graph);
/* let max_size = graph.directories().map(|x| x.size()).max();
println!("Max size: {:?}", max_size);*/
//println!("{:?}", graph); //println!("{:?}", graph);
Ok(()) Ok(())

Loading…
Cancel
Save