master
Avril 4 years ago
parent 784f3f2c10
commit c9d71b0bec
Signed by: flanchan
GPG Key ID: 284488987C31F630

7
Cargo.lock generated

@ -162,6 +162,7 @@ dependencies = [
"serde_cbor", "serde_cbor",
"smallvec", "smallvec",
"tokio", "tokio",
"treemap",
] ]
[[package]] [[package]]
@ -781,6 +782,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "treemap"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1571f89da27a5e1aa83304ee1ab9519ea8c6432b4c8903aaaa6c9a9eecb6f36"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.7.1" version = "1.7.1"

@ -13,7 +13,7 @@ codegen-units = 1
panic = "unwind" panic = "unwind"
[features] [features]
default = ["splash", "inspect", "defer-drop", "jemalloc", "prealloc"] default = ["splash", "inspect", "defer-drop", "jemalloc", "prealloc", "treemap"]
# When using the REPL to inspect graphs, save command history. # When using the REPL to inspect graphs, save command history.
save-history = [] save-history = []
@ -52,3 +52,4 @@ serde = {version = "1.0.123", features=["derive"], optional=true}
serde_cbor = {version = "0.11.1", optional=true} serde_cbor = {version = "0.11.1", optional=true}
smallvec = "1.6.1" smallvec = "1.6.1"
tokio = {version = "0.2", features=["full"]} tokio = {version = "0.2", features=["full"]}
treemap = {version = "0.3.2", optional=true}

@ -6,6 +6,33 @@ use std::fmt;
#[cfg(feature="inspect")] use config::OutputSerialisationMode; #[cfg(feature="inspect")] use config::OutputSerialisationMode;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InspectKind
{
Treemap(Option<(u64, u64)>),
}
impl fmt::Display for InspectKind
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Treemap(None) => write!(f, "treemap"),
Self::Treemap(Some((x,y))) => write!(f, "treemap:{}:{}", x, y), // Width and height.
}
}
}
impl std::str::FromStr for InspectKind
{
type Err = eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err>
{
todo!()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Argument pub enum Argument
{ {
@ -29,6 +56,8 @@ pub enum Argument
StopReading, StopReading,
Inspect(InspectKind),
Input(String), Input(String),
} }
@ -70,6 +99,9 @@ impl Argument
{ {
use Argument::*; use Argument::*;
match self { match self {
Inspect(InspectKind::Treemap(None)) => cfg.inspection.treemap = Some((640, 480)),
Inspect(InspectKind::Treemap(x)) => cfg.inspection.treemap = x,
LimitConcMaxProc => cfg.max_tasks = config::max_tasks_cpus(), LimitConcMaxProc => cfg.max_tasks = config::max_tasks_cpus(),
LimitConc(max) => cfg.max_tasks = Some(max), LimitConc(max) => cfg.max_tasks = Some(max),
UnlimitConc => cfg.max_tasks = None, UnlimitConc => cfg.max_tasks = None,
@ -108,6 +140,8 @@ impl fmt::Display for Argument
use Argument::*; use Argument::*;
match self match self
{ {
Inspect(ins) => write!(f, "--inspect {}", ins),
ModeChangeHelp => write!(f, "--help"), ModeChangeHelp => write!(f, "--help"),
LimitConcMaxProc => write!(f, "-m"), LimitConcMaxProc => write!(f, "-m"),
LimitConc(limit) => write!(f, "--threads {}", limit), LimitConc(limit) => write!(f, "--threads {}", limit),
@ -365,7 +399,12 @@ where I: Iterator<Item=String>
let mut keep_reading = Continue::Yes; let mut keep_reading = Continue::Yes;
let item = match this.trim() let item = match this.trim()
{ {
"--threads" => { "--inspect" => {
let ins = args.next().ok_or(eyre!("`--inspect` expects a parameter"))
.with_suggestion(suggestion_intended_arg.clone())?;
Argument::Inspect(ins.parse().wrap_err(eyre!("Failed to parse parameter for `--inspect`"))?)
},
" --threads" => {
let max = args.next().ok_or(eyre!("`--threads` expects a parameter")) let max = args.next().ok_or(eyre!("`--threads` expects a parameter"))
.with_suggestion(suggestion_intended_arg.clone())?; .with_suggestion(suggestion_intended_arg.clone())?;
match NonZeroUsize::new(max.parse::<usize>() match NonZeroUsize::new(max.parse::<usize>()

@ -121,6 +121,25 @@ impl OutputSerialisationMode
} }
} }
/// What to do with the graph afterwards?
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Inspection
{
pub treemap: Option<(u64, u64)>, //w and h
}
impl Default for Inspection
{
#[inline]
fn default() -> Self
{
Self {
treemap: None
}
}
}
/// Configuration for this run /// Configuration for this run
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config pub struct Config
@ -133,6 +152,8 @@ pub struct Config
#[cfg(feature="inspect")] #[cfg(feature="inspect")]
pub serialise_output: Option<OutputSerialisationMode>, pub serialise_output: Option<OutputSerialisationMode>,
pub inspection: Inspection,
} }
impl Config impl Config
@ -188,6 +209,8 @@ impl Default for Config
output_level: Default::default(), output_level: Default::default(),
#[cfg(feature="inspect")] #[cfg(feature="inspect")]
serialise_output: None, serialise_output: None,
inspection: Default::default(),
} }
} }
} }

@ -15,8 +15,123 @@ pub struct INodeInfoGraph
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
} }
#[derive(Debug, Clone)]
pub struct INodeInfoGraphEntry<'a>
{
master: &'a INodeInfoGraph,
inode: INode,
}
impl<'a> INodeInfoGraphEntry<'a>
{
/// Create an iterator over children of this graph item.
///
/// # Note
/// Returns `None` if this is not a directory item.
pub fn level(&self) -> Option<Level<'a>>
{
match self.master.inodes.get(&self.inode)
{
Some(FsInfo::Directory(parent)) => {
Some(
Level{
master: self.master,
inode: self.inode.clone(),
children: match self.master.children.get(&self.inode) {
Some(iter) => iter.iter(),
_ => [].iter(),
},
}
)
},
Some(FsInfo::File(_, _)) => None,
None => {
panic!("No lookup information for inode {:?}", self.inode)
}//Some(Level{master: self.master, inode: None, children: [].iter()}),
}
}
/// The `FsInfo` for this item.
pub fn info(&self) -> &FsInfo
{
self.master.inodes.get(&self.inode).unwrap()
}
/// The path for this inode
pub fn path(&self) -> &PathBuf
{
self.master.paths_reverse.get(&self.inode).unwrap()
}
}
#[derive(Debug, Clone)]
pub struct Level<'a>
{
master: &'a INodeInfoGraph,
inode: INode,// Should only ever be `Directory` for `Level`.
children: std::slice::Iter<'a, INode>,
}
#[derive(Debug, Clone)]
pub struct TopLevel<'a>
{
master: &'a INodeInfoGraph,
children: std::vec::IntoIter<&'a INode>,
}
impl<'a> Iterator for TopLevel<'a>
{
type Item = INodeInfoGraphEntry<'a>;
fn next(&mut self) -> Option<Self::Item>
{
let master = self.master;
self.children.next().map(|inode| {
INodeInfoGraphEntry{
master,
inode: inode.clone(),
}
})
}
}
impl<'a> Iterator for Level<'a>
{
type Item = INodeInfoGraphEntry<'a>;
fn next(&mut self) -> Option<Self::Item>
{
let master = self.master;
self.children.next().map(|inode| {
INodeInfoGraphEntry{
master,
inode: inode.clone(),
}
})
}
}
impl INodeInfoGraph impl INodeInfoGraph
{ {
/// Total size of all files
pub fn total_size(&self) -> u64
{
self.inodes.iter().map(|(_, v)| {
match v {
FsInfo::File(len, _) => *len,
_ => 0,
}
}).sum()
}
/// An iterator over the top level of items
pub fn top_level(&self) -> TopLevel<'_>
{
let top_level = self.inodes.iter().filter(|(node, _)| {
!self.inodes.contains_key(node)
});
TopLevel {
master: self,
children: top_level.map(|(k, _)| k).collect::<Vec<_>>().into_iter(),
}
}
/// 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
{ {
@ -48,6 +163,11 @@ impl INodeInfoGraph
self.paths_reverse.extend(self.paths.iter().map(|(x,y)| (*y,x.clone()))); self.paths_reverse.extend(self.paths.iter().map(|(x,y)| (*y,x.clone())));
self self
} }
/// Total number of items in the graph
#[inline] pub fn len(&self) -> usize
{
self.children.len()
}
/// Get the FsInfo of this `INode` /// Get the FsInfo of this `INode`
#[inline] pub fn get_info(&self, node: impl Borrow<INode>) -> Option<&FsInfo> #[inline] pub fn get_info(&self, node: impl Borrow<INode>) -> Option<&FsInfo>
{ {
@ -61,13 +181,13 @@ impl INodeInfoGraph
/*Children(self, match self.children.get(node.borrow()) { /*Children(self, match self.children.get(node.borrow()) {
Some(slc) => slc.iter(), Some(slc) => slc.iter(),
_ => [].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<'_>
{ {
todo!()//Directories(self, self.children.keys()) todo!()//Directories(self, self.children.keys())
}*/ }*/
/// Convert into a hierarchical representation /// Convert into a hierarchical representation
pub fn into_hierarchical(self) -> HierarchicalINodeGraph pub fn into_hierarchical(self) -> HierarchicalINodeGraph
@ -264,6 +384,12 @@ impl HierarchicalINodeGraph
hi.len().map(|x| (path.as_path(), x)) hi.len().map(|x| (path.as_path(), x))
}).max_by_key(|x| x.1) }).max_by_key(|x| x.1)
} }
/// Complete number of items in the tree
#[inline] pub fn len(&self) -> usize
{
self.table.len()
}
} }
impl From<INodeInfoGraph> for HierarchicalINodeGraph impl From<INodeInfoGraph> for HierarchicalINodeGraph

@ -18,6 +18,7 @@ pub use graph::{
INodeInfoGraph, INodeInfoGraph,
HierarchicalINodeGraph, HierarchicalINodeGraph,
FsObjKind as FsKind, FsObjKind as FsKind,
INodeInfoGraphEntry,
}; };
/// A raw file or directory inode number /// A raw file or directory inode number

@ -238,11 +238,11 @@ impl<I: Iterator> Iterator for OptionIterator<I>
type Item = I::Item; type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> fn next(&mut self) -> Option<Self::Item>
{ {
self.0.map(|x| x.next()).flatten() self.0.as_mut().map(|x| x.next()).flatten()
} }
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 { match self.0.as_ref() {
Some(i) => i.size_hint(), Some(i) => i.size_hint(),
_ => (0, Some(0)), _ => (0, Some(0)),
} }
@ -257,7 +257,7 @@ impl<I: Iterator> DoubleEndedIterator for OptionIterator<I>
where I: DoubleEndedIterator where I: DoubleEndedIterator
{ {
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
self.0.map(|x| x.next_back()).flatten() self.0.as_mut().map(|x| x.next_back()).flatten()
} }
} }

@ -0,0 +1,124 @@
use super::*;
use treemap::{
Rect,
Mappable,
TreemapLayout
};
use data::{FsInfo, INodeInfoGraph, INodeInfoGraphEntry};
/// A treemap of all **files** in the graph.
#[derive(Debug, Clone, PartialEq)]
pub struct Treemap
{
//layout: TreemapLayout,
nodes: Vec<MapNode>,
}
impl Treemap
{
/// All nodes of the map.
#[inline] pub fn nodes(&self) -> &[MapNode]
{
&self.nodes[..]
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MapNode
{
name: String,
vw_size: f64, // Should be halved each iteration
vw_bounds: Rect, // should be Rect::new() before aligntment
}
impl MapNode
{
/// The calculated bounds of the node
#[inline] pub fn bounds(&self) -> &Rect
{
&self.vw_bounds
}
/// The name of the node
#[inline] pub fn name(&self) -> &str
{
&self.name[..]
}
#[inline] fn size(&self) -> f64 //Is this useful for consumer?
{
self.vw_size
}
#[inline] fn new(name: String) -> Self
{
Self {
vw_size: 1.0,
vw_bounds: Rect::new(),
name,
}
}
}
/// Create a treemap from this graph.
pub fn treemap(_cfg: &Config, graph: &INodeInfoGraph, (w, h): (f64, f64)) -> Treemap
{
let layout = TreemapLayout::new();
let mut nodes = Vec::with_capacity(graph.len());
//TODO: Recursively walk the graph, halving size with each iteration. (Maybe we need `INodeInfoGraph` here, not `Hierarchicalinodegraph`?)
let total_size = graph.total_size();
let size = 1.0;
fn calc_path<'a, I: IntoIterator<Item = INodeInfoGraphEntry<'a>>>(insert: &'a mut Vec<MapNode>, from: I, total_size: u64, size: f64, scale: f64)
{
for top in from {
let path = top.path();
match top.info() {
FsInfo::Directory(_) => {
//TODO: Do we add dir itself? I think not?
// Add children
let size = size * 0.5;
calc_path(insert, top.level().unwrap(), total_size, size, scale);
},
&FsInfo::File(sz, _) => {
let fract = (sz as f64) / (total_size as f64);
insert.push(MapNode {
name: path.to_string_lossy().into_owned(),
vw_size: fract * scale,
vw_bounds: Rect::new(),
})
},
}
}
}
calc_path(&mut nodes, graph.top_level(), total_size, size, 1.0);
layout.layout_items(&mut nodes[..], Rect {
x: 0.0,
y: 0.0,
w, h,
});
Treemap {
//layout,
nodes
}
}
impl Mappable for MapNode
{
fn size(&self) -> f64
{
self.vw_size
}
fn bounds(&self) -> &Rect
{
&self.vw_bounds
}
fn set_bounds(&mut self, bounds: Rect)
{
self.vw_bounds = bounds;
}
}

@ -4,7 +4,7 @@ use super::*;
use data::HierarchicalINodeGraph; use data::HierarchicalINodeGraph;
use config::Config; use config::Config;
pub mod repl; //pub mod repl;
/// Print the most basic info /// Print the most basic info
pub fn print_basic_max_info(cfg: &Config, graph: &HierarchicalINodeGraph) pub fn print_basic_max_info(cfg: &Config, graph: &HierarchicalINodeGraph)
@ -13,3 +13,9 @@ pub fn print_basic_max_info(cfg: &Config, graph: &HierarchicalINodeGraph)
cfg_println!(Quiet; cfg, "Max size dir: {:?}", graph.path_max_size_for(data::FsKind::Directory)); cfg_println!(Quiet; cfg, "Max size dir: {:?}", graph.path_max_size_for(data::FsKind::Directory));
cfg_println!(Quiet; cfg, "Max size all: {:?}", graph.path_max_size()); cfg_println!(Quiet; cfg, "Max size all: {:?}", graph.path_max_size());
} }
#[cfg(feature="treemap")]
mod map;
#[cfg(feature="treemap")]
pub use map::*;

@ -127,13 +127,31 @@ async fn normal(cfg: config::Config) -> eyre::Result<()>
}; };
let cfg = cfg.make_global(); let cfg = cfg.make_global();
let graph = tokio::task::spawn_blocking(move || { let (treemap, graph) = tokio::task::spawn_blocking(move || {
cfg_println!(Verbose; cfg, "Computing hierarchy..."); cfg_println!(Verbose; cfg, "Computing hierarchy...");
// Create treemap
#[cfg(feature="treemap")]
let treemap = {
//Check config for if the treemap flag is present. If it is, get the width and height from there too.
if let Some(&(x,y)) = cfg.inspection.treemap.as_ref() {
Some(info::treemap(cfg, &graph, (
x as f64,
y as f64,
)))
} else {
None
}
};
#[cfg(not(feature="treemap"))]
let treemap = Option::<std::convert::Infallible>::None;
// Create recursive graph
let mut graph = graph.into_hierarchical(); let mut graph = graph.into_hierarchical();
cfg_println!(Verbose; cfg, "Computing sizes..."); cfg_println!(Verbose; cfg, "Computing sizes...");
graph.compute_recursive_sizes(); graph.compute_recursive_sizes();
graph (treemap, graph)
}).await.expect("Failed to compute hierarchy from graph"); }).await.expect("Failed to compute hierarchy from graph");
//#[cfg(debug_assertions)] cfg_eprintln!(Verbose; cfg, "{:?}", graph); //#[cfg(debug_assertions)] cfg_eprintln!(Verbose; cfg, "{:?}", graph);
@ -155,6 +173,12 @@ async fn normal(cfg: config::Config) -> eyre::Result<()>
} }
}; };
#[cfg(feature="treemap")]
if let Some(treemap) = treemap {
//TODO: Print treemap
todo!("{:?}",treemap);
}
info::print_basic_max_info(&cfg, &graph); info::print_basic_max_info(&cfg, &graph);
#[cfg(feature="inspect")] #[cfg(feature="inspect")]

Loading…
Cancel
Save