Improve allocation pattern

This commit is contained in:
Riyyi
2026-08-02 15:55:53 +02:00
parent b38caa7ff8
commit c4cd64cf44
+47 -30
View File
@@ -1,5 +1,6 @@
package file
import "base:runtime"
import "core:fmt"
import "core:os"
import "core:strings"
@@ -7,40 +8,56 @@ import "core:strings"
// -----------------------------------------
DirectoryEntry :: struct #all_or_none {
relpath: string,
relpath: string, // NOTE: owning string
size: i64,
}
// -----------------------------------------
list_dir_recursive :: proc(f: ^os.File) -> [dynamic]DirectoryEntry {
// Returning `[dynamic]DirectoryEntry` and each relpath `string` inside it are owned by the caller.
list_dir_recursive :: proc(
f: ^os.File,
allocator := context.allocator,
) -> [dynamic]DirectoryEntry {
path := os.name(f)
return list_dir_recursive_by_path_impl(path)
return list_dir_recursive_by_path_impl(path, allocator)
}
list_dir_recursive_by_path :: proc(path: string) -> [dynamic]DirectoryEntry {
return list_dir_recursive_by_path_impl(path)
// Returning `[dynamic]DirectoryEntry` and each relpath `string` inside it are owned by the caller.
list_dir_recursive_by_path :: proc(
path: string,
allocator := context.allocator,
) -> [dynamic]DirectoryEntry {
return list_dir_recursive_by_path_impl(path, allocator)
}
@(private)
list_dir_recursive_by_path_impl :: proc(
path: string,
allocator: runtime.Allocator
) -> [dynamic]DirectoryEntry {
if !os.is_dir(path) {
fmt.eprintln("error: path is not a directory:", path)
os.exit(1)
}
result := [dynamic]DirectoryEntry{}
working_dir, wd_err := os.get_working_directory(allocator)
if wd_err != nil {
fmt.eprintln("error: get working directory failed:", wd_err)
os.exit(1)
}
defer delete(working_dir, allocator)
queue := [dynamic]string{}
append(&queue, path)
result := make([dynamic]DirectoryEntry, allocator)
queue := make([dynamic]string, 0, 1, allocator) // reserve 1 slot
defer delete(queue) // dynamic array embeds allocator
append(&queue, strings.clone(path))
for len(queue) > 0 {
list_dir_queue(&queue, &result)
list_dir_queue(&queue, &result, working_dir, allocator)
}
shrink(&result, len(result)) // shrink to fit
return result
}
@@ -48,10 +65,13 @@ list_dir_recursive_by_path_impl :: proc(
list_dir_queue :: proc(
queue: ^[dynamic]string,
result: ^[dynamic]DirectoryEntry,
working_dir: string,
allocator: runtime.Allocator,
) {
assert(len(queue) > 0, "queue must not be empty")
path := pop(queue)
defer delete(path, queue^.allocator)
f, open_err := os.open(path, {.Read})
if open_err != nil {
@@ -60,41 +80,38 @@ list_dir_queue :: proc(
}
defer os.close(f)
entries, read_err := os.read_all_directory(f, context.allocator)
entries, read_err := os.read_all_directory(f, allocator)
if read_err != nil {
fmt.eprintln("error: read directory failed:", read_err)
os.exit(1)
}
defer delete(entries)
defer delete(entries, allocator)
// Reserve space for new entries
reserve(result, cap(result) + len(entries))
// Reserve space for new entries, minor waste as directories are also counted.
reserve(result, len(result) + len(entries))
working_dir, wd_err := os.get_working_directory(context.allocator)
if wd_err != nil {
fmt.eprintln("error: get working directory failed:", wd_err)
os.exit(1)
}
defer delete(working_dir)
for entry, i in entries {
if entry.type == os.File_Type.Directory {
append(queue, entry.fullpath)
}
if entry.type == os.File_Type.Regular {
relpath, rel_err := strings.replace(
for entry in entries {
#partial switch entry.type {
case os.File_Type.Directory:
append(queue, strings.clone(entry.fullpath, queue^.allocator))
case os.File_Type.Regular:
{
relpath, was_allocation := strings.replace(
entry.fullpath,
working_dir,
".",
1,
context.allocator,
result^.allocator,
)
if !was_allocation {
relpath = strings.clone(relpath, result^.allocator)
}
append(
result,
DirectoryEntry{relpath = relpath, size = entry.size},
)
}
case: // skip other types
}
}
}