commit 0e96291b65c4c9de2ef9a858cf45d5fe85349977
parent 262badd3caa0e0906782e324cd98f02de2a276df
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Sun, 20 Sep 2026 16:43:22 -0300
main: charge the tree once, however it was read
The batch path had its own roll-up beside the incremental one the builder uses.
Two answers to "what does this directory hold", agreeing only because their output
was compared, and differing in a detail nobody chose: the batch copy charged a
node whose parent never resolved, the incremental one waits for a settled chain.
Batch now runs the same charging, once, over a tree nobody is still filling.
Neither engine's output moves, on /usr, /home/jfm or a 491 GiB volume. Ranking is
bounded by what has been charged rather than by the tree, which is what the live
path already did.
Diffstat:
| M | main.odin | | | 52 | ++++++++++++++-------------------------------------- |
1 file changed, 14 insertions(+), 38 deletions(-)
diff --git a/main.odin b/main.odin
@@ -155,43 +155,18 @@ run :: proc() -> int {
time.duration_milliseconds(elapsed),
)
- totals := make([]u64, t.count)
- defer delete(totals)
- roll_up(&t, totals)
-
- print_largest(&t, totals, .files)
- print_largest(&t, totals, .directories)
+ // The same charging the live scan does, run once over a tree nobody is still
+ // filling. A node left uncharged here has an ancestor that never resolved, and
+ // another pass would not change that.
+ roll: Rollup
+ defer rollup_destroy(&roll)
+ rollup_advance(&roll, &t)
+
+ print_largest(&t, roll.totals[:], .files)
+ print_largest(&t, roll.totals[:], .directories)
return 0
}
-/*
-Credit every node's bytes to each of its ancestors.
-
-An extra name for a file counted elsewhere is skipped, or the file would be charged
-to every directory it is linked into. The depth cap guards against a cycle that a
-corrupt source could produce.
-*/
-roll_up :: proc(t: ^scan.Tree, totals: []u64) {
- for i in 0 ..< t.count {
- n := scan.node(t, i)
- if .Used not_in n.flags || .Extra_Name in n.flags {
- continue
- }
- if .Directory in n.flags {
- totals[i] += n.disk
- }
- cur := i
- for depth := 0; depth < 64; depth += 1 {
- p := scan.node(t, cur).parent
- if p == cur || p >= t.count {
- break
- }
- totals[p] += n.disk
- cur = p
- }
- }
-}
-
Kind :: enum {
files,
directories,
@@ -218,20 +193,21 @@ rank :: proc(t: ^scan.Tree, totals: []u64, top: []Sized) -> int {
print_largest :: proc(t: ^scan.Tree, totals: []u64, kind: Kind) {
top: [TOP_N]Sized
count := 0
- for i in 0 ..< t.count {
+ for i in 0 ..< u32(len(totals)) {
n := scan.node(t, i)
- if .Used not_in n.flags {
+ flags := scan.node_flags(n)
+ if .Used not_in flags {
continue
}
bytes: u64
switch kind {
case .files:
- if .Directory in n.flags || .Extra_Name in n.flags {
+ if .Directory in flags || .Extra_Name in flags {
continue
}
bytes = n.disk
case .directories:
- if .Directory not_in n.flags {
+ if .Directory not_in flags {
continue
}
bytes = totals[i]