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 package file
import "base:runtime"
import "core:fmt" import "core:fmt"
import "core:os" import "core:os"
import "core:strings" import "core:strings"
@@ -7,40 +8,56 @@ import "core:strings"
// ----------------------------------------- // -----------------------------------------
DirectoryEntry :: struct #all_or_none { DirectoryEntry :: struct #all_or_none {
relpath: string, relpath: string, // NOTE: owning string
size: i64, 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) 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 { // Returning `[dynamic]DirectoryEntry` and each relpath `string` inside it are owned by the caller.
return list_dir_recursive_by_path_impl(path) list_dir_recursive_by_path :: proc(
path: string,
allocator := context.allocator,
) -> [dynamic]DirectoryEntry {
return list_dir_recursive_by_path_impl(path, allocator)
} }
@(private) @(private)
list_dir_recursive_by_path_impl :: proc( list_dir_recursive_by_path_impl :: proc(
path: string, path: string,
allocator: runtime.Allocator
) -> [dynamic]DirectoryEntry { ) -> [dynamic]DirectoryEntry {
if !os.is_dir(path) { if !os.is_dir(path) {
fmt.eprintln("error: path is not a directory:", path) fmt.eprintln("error: path is not a directory:", path)
os.exit(1) 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{} result := make([dynamic]DirectoryEntry, allocator)
append(&queue, path)
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 { 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 return result
} }
@@ -48,10 +65,13 @@ list_dir_recursive_by_path_impl :: proc(
list_dir_queue :: proc( list_dir_queue :: proc(
queue: ^[dynamic]string, queue: ^[dynamic]string,
result: ^[dynamic]DirectoryEntry, result: ^[dynamic]DirectoryEntry,
working_dir: string,
allocator: runtime.Allocator,
) { ) {
assert(len(queue) > 0, "queue must not be empty") assert(len(queue) > 0, "queue must not be empty")
path := pop(queue) path := pop(queue)
defer delete(path, queue^.allocator)
f, open_err := os.open(path, {.Read}) f, open_err := os.open(path, {.Read})
if open_err != nil { if open_err != nil {
@@ -60,41 +80,38 @@ list_dir_queue :: proc(
} }
defer os.close(f) 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 { if read_err != nil {
fmt.eprintln("error: read directory failed:", read_err) fmt.eprintln("error: read directory failed:", read_err)
os.exit(1) os.exit(1)
} }
defer delete(entries) defer delete(entries, allocator)
// Reserve space for new entries // Reserve space for new entries, minor waste as directories are also counted.
reserve(result, cap(result) + len(entries)) reserve(result, len(result) + len(entries))
working_dir, wd_err := os.get_working_directory(context.allocator) for entry in entries {
if wd_err != nil { #partial switch entry.type {
fmt.eprintln("error: get working directory failed:", wd_err) case os.File_Type.Directory:
os.exit(1) append(queue, strings.clone(entry.fullpath, queue^.allocator))
} case os.File_Type.Regular:
defer delete(working_dir) {
relpath, was_allocation := strings.replace(
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(
entry.fullpath, entry.fullpath,
working_dir, working_dir,
".", ".",
1, 1,
context.allocator, result^.allocator,
) )
if !was_allocation {
relpath = strings.clone(relpath, result^.allocator)
}
append( append(
result, result,
DirectoryEntry{relpath = relpath, size = entry.size}, DirectoryEntry{relpath = relpath, size = entry.size},
) )
} }
case: // skip other types
}
} }
} }