You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dirstat/src/data/graph.rs

211 lines
5.4 KiB

use super::*;
use std::collections::HashMap;
use std::path::{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)]
pub struct INodeInfoGraph
{
inodes: HashMap<INode, FsInfo>, // FsInfo contains parent INode that can be used to look up again in this table
paths: HashMap<PathBuf, INode>, // map absolute paths to INodes to be looked up in `inodes` table.
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,
}
.compute_child_table()
.compute_reverse_path_table()
}
#[inline] fn compute_child_table(mut self) -> Self
{
for (node, info) in self.inodes.iter()
{
match info {
FsInfo::Directory(parent_node) |
FsInfo::File(_, parent_node) => {
self.children.entry(*parent_node).or_insert_with(|| Vec::new()).push(*node);
},
}
}
self
}
#[inline] fn compute_reverse_path_table(mut self) -> Self
{
self.paths_reverse.extend(self.paths.iter().map(|(x,y)| (*y,x.clone())));
self
}
/// Get the FsInfo of this `INode`
#[inline] pub fn get_info(&self, node: impl Borrow<INode>) -> Option<&FsInfo>
{
self.inodes.get(node.borrow())
}
/// An iterator over top-level children of this node
pub fn children_of(&self, node: impl Borrow<INode>) -> Children<'_>
{
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<'_>
{
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>
{
self.1.next().map(|x| INodeRef(self.0, *x))
}
#[inline] fn size_hint(&self) -> (usize, Option<usize>) {
self.1.size_hint()
}
}
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>);
impl<'a> Iterator for Children<'a>
{
type Item = INodeRef<'a>;
#[inline] fn next(&mut self) -> Option<Self::Item>
{
self.1.next().map(|x| INodeRef(self.0, *x))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.1.size_hint()
}
}
impl<'a> ExactSizeIterator for Children<'a>{}
impl<'a> FusedIterator for Children<'a>{}
impl<'a> DoubleEndedIterator for Children<'a>
{
#[inline] fn next_back(&mut self) -> Option<Self::Item> {
self.1.next_back().map(|x| INodeRef(self.0, *x))
}
}