Improve allocation pattern
This commit is contained in:
+56
-39
@@ -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 {
|
entry.fullpath,
|
||||||
|
working_dir,
|
||||||
if entry.type == os.File_Type.Directory {
|
".",
|
||||||
append(queue, entry.fullpath)
|
1,
|
||||||
}
|
result^.allocator,
|
||||||
|
)
|
||||||
if entry.type == os.File_Type.Regular {
|
if !was_allocation {
|
||||||
relpath, rel_err := strings.replace(
|
relpath = strings.clone(relpath, result^.allocator)
|
||||||
entry.fullpath,
|
}
|
||||||
working_dir,
|
append(
|
||||||
".",
|
result,
|
||||||
1,
|
DirectoryEntry{relpath = relpath, size = entry.size},
|
||||||
context.allocator,
|
)
|
||||||
)
|
}
|
||||||
append(
|
case: // skip other types
|
||||||
result,
|
|
||||||
DirectoryEntry{relpath = relpath, size = entry.size},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user