From c4cd64cf44a572153b32010c3c1811ac4a81ac15 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Sun, 2 Aug 2026 14:29:18 +0200 Subject: [PATCH] Improve allocation pattern --- src/file/list_dir.odin | 95 +++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/src/file/list_dir.odin b/src/file/list_dir.odin index 2dc86bf..be676d3 100644 --- a/src/file/list_dir.odin +++ b/src/file/list_dir.odin @@ -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( - entry.fullpath, - working_dir, - ".", - 1, - context.allocator, - ) - append( - result, - DirectoryEntry{relpath = relpath, size = entry.size}, - ) + 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, + result^.allocator, + ) + if !was_allocation { + relpath = strings.clone(relpath, result^.allocator) + } + append( + result, + DirectoryEntry{relpath = relpath, size = entry.size}, + ) + } + case: // skip other types } } }