commit cf8be8d72882a8164f7685ce206f6773d1af058f
parent 5e1620f451e18659038ee547fb0e2e4dedc99b12
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Sun, 20 Sep 2026 16:06:05 -0300
walk: name the entry point for what it reads
`scan` collided with the package of the same name that it writes into, to the
point that the tests had to import that package under an alias to keep the name
free. `read` sits beside ntfs.read_mft and leaves the collision behind.
Its docstring described the concurrency shape it happens to be built on rather
than what calling it does. It now says what the tree holds afterwards and what
each error means for what is in it.
Diffstat:
4 files changed, 34 insertions(+), 32 deletions(-)
diff --git a/fill.odin b/fill.odin
@@ -157,5 +157,5 @@ fill_mft_read :: proc(f: ^Fill) {
fill_walk :: proc(f: ^Fill) {
root := scan.location(f.target, context.allocator)
defer delete(root)
- f.walk_err = walk.scan(root, f.tree, f.wcfg)
+ f.walk_err = walk.read(root, f.tree, f.wcfg)
}
diff --git a/walk/read_linux_test.odin b/walk/read_linux_test.odin
@@ -6,7 +6,7 @@ import "core:os"
import "core:slice"
import "core:testing"
-import shared "../scan"
+import "../scan"
/*
A hole costs nothing and has to be charged nothing.
@@ -40,13 +40,13 @@ test_a_sparse_file_is_charged_what_it_occupies :: proc(t: ^testing.T) {
"could not write",
)
- tree: shared.Tree
- testing.expect_value(t, shared.tree_init(&tree, 1), shared.Error.None)
- defer shared.tree_destroy(&tree)
- testing.expect_value(t, scan(root, &tree, Config{}), Error.None)
+ tree: scan.Tree
+ testing.expect_value(t, scan.tree_init(&tree, 1), scan.Error.None)
+ defer scan.tree_destroy(&tree)
+ testing.expect_value(t, read(root, &tree, Config{}), Error.None)
for i in 0 ..< tree.count {
- n := shared.node(&tree, i)
+ n := scan.node(&tree, i)
switch n.name {
case "hole.bin":
testing.expect_value(t, n.size, u64(1 << 20))
diff --git a/walk/walk.odin b/walk/walk.odin
@@ -36,14 +36,18 @@ Error :: enum {
}
/*
-Walk `root` into the tree.
+Read everything under `root` into the tree.
-Directories are the unit of work and each yields more of them, which is the shape
-`flow.manage` exists for: a worker takes the next directory the moment one is known
-rather than waiting for a level to finish. Every worker writes only into slots it
-claimed, so nothing here is synchronised except the queue itself.
+Every directory below it is opened, each entry it holds becomes a node, and each
+directory among them is read in turn. A worker takes the next directory the moment
+one is known rather than waiting for a level to finish, so a tree that is wide near
+the root and one that is deep in a single branch both keep them busy.
+
+Returns once the tree holds everything under `root`; Cancelled if the scan was called
+off part way, and Out_Of_Memory if the tree ran out of room, in which case what is
+there is short rather than wrong.
*/
-scan :: proc(root: string, t: ^scan.Tree, cfg := Config{}) -> Error {
+read :: proc(root: string, t: ^scan.Tree, cfg := Config{}) -> Error {
allocator := context.allocator
w := scan.writer(t, 0)
diff --git a/walk/walk_test.odin b/walk/walk_test.odin
@@ -5,9 +5,7 @@ import "core:os"
import "core:slice"
import "core:testing"
-// This package's own entry point is called `scan` as well, so the import is
-// aliased to leave that name free to be called.
-import shared "../scan"
+import "../scan"
/*
Build a known tree and return its root.
@@ -53,11 +51,11 @@ fixture :: proc(t: ^testing.T, name: string) -> string {
// Every node the walk recorded, by name. Names repeat across directories in general;
// none of them do in the fixture.
@(private = "file")
-named :: proc(tree: ^shared.Tree, name: string) -> (^shared.Node, int) {
- found: ^shared.Node
+named :: proc(tree: ^scan.Tree, name: string) -> (^scan.Node, int) {
+ found: ^scan.Node
count := 0
for i in 0 ..< tree.count {
- n := shared.node(tree, i)
+ n := scan.node(tree, i)
if .Used in n.flags && n.name == name {
found = n
count += 1
@@ -67,10 +65,10 @@ named :: proc(tree: ^shared.Tree, name: string) -> (^shared.Node, int) {
}
@(private = "file")
-walked :: proc(t: ^testing.T, root: string, cfg := Config{}) -> shared.Tree {
- tree: shared.Tree
- testing.expect_value(t, shared.tree_init(&tree, 1), shared.Error.None)
- testing.expect_value(t, scan(root, &tree, cfg), Error.None)
+walked :: proc(t: ^testing.T, root: string, cfg := Config{}) -> scan.Tree {
+ tree: scan.Tree
+ testing.expect_value(t, scan.tree_init(&tree, 1), scan.Error.None)
+ testing.expect_value(t, read(root, &tree, cfg), Error.None)
return tree
}
@@ -79,7 +77,7 @@ test_walk_records_every_entry :: proc(t: ^testing.T) {
root := fixture(t, "every")
defer os.remove_all(root)
tree := walked(t, root)
- defer shared.tree_destroy(&tree)
+ defer scan.tree_destroy(&tree)
// The root, plus the six entries below it.
testing.expect_value(t, tree.count, u32(7))
@@ -99,7 +97,7 @@ test_walk_marks_directories_and_sizes :: proc(t: ^testing.T) {
root := fixture(t, "shapes")
defer os.remove_all(root)
tree := walked(t, root)
- defer shared.tree_destroy(&tree)
+ defer scan.tree_destroy(&tree)
sub, _ := named(&tree, "sub")
testing.expect(t, .Directory in sub.flags, "sub is not marked a directory")
@@ -121,7 +119,7 @@ test_walk_does_not_follow_a_symlink_by_default :: proc(t: ^testing.T) {
root := fixture(t, "nofollow")
defer os.remove_all(root)
tree := walked(t, root)
- defer shared.tree_destroy(&tree)
+ defer scan.tree_destroy(&tree)
link, _ := named(&tree, "link")
testing.expect(t, .Reparse in link.flags, "the symlink is not marked a reparse")
@@ -136,7 +134,7 @@ test_walk_follows_a_symlink_when_asked :: proc(t: ^testing.T) {
root := fixture(t, "follow")
defer os.remove_all(root)
tree := walked(t, root, Config{follow = true})
- defer shared.tree_destroy(&tree)
+ defer scan.tree_destroy(&tree)
// sub reached twice: once by name, once through the link. Its three entries
// therefore appear twice over.
@@ -150,12 +148,12 @@ test_walk_stops_when_cancelled :: proc(t: ^testing.T) {
root := fixture(t, "cancel")
defer os.remove_all(root)
- tree: shared.Tree
- testing.expect_value(t, shared.tree_init(&tree, 1), shared.Error.None)
- defer shared.tree_destroy(&tree)
+ tree: scan.Tree
+ testing.expect_value(t, scan.tree_init(&tree, 1), scan.Error.None)
+ defer scan.tree_destroy(&tree)
// Called off before it starts, so the seed directory is the only work offered
// and it declines it.
- shared.cancel(&tree)
- testing.expect_value(t, scan(root, &tree, Config{}), Error.Cancelled)
+ scan.cancel(&tree)
+ testing.expect_value(t, read(root, &tree, Config{}), Error.Cancelled)
}