From d4af7b86a7a41d7a59c111d2eb22fdd0e8686e78 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 24 Jul 2024 14:19:30 +0100 Subject: [PATCH] Begin cleaning up `os2.read_directory` --- core/os/os2/dir.odin | 65 ++++++++++++- core/os/os2/dir_linux.odin | 18 +++- core/os/os2/dir_windows.odin | 174 ++++++++++++++++++++--------------- core/os/os2/stat.odin | 12 ++- 4 files changed, 186 insertions(+), 83 deletions(-) diff --git a/core/os/os2/dir.odin b/core/os/os2/dir.odin index 5f9deb08a..6334ee7b8 100644 --- a/core/os/os2/dir.odin +++ b/core/os/os2/dir.odin @@ -1,21 +1,80 @@ package os2 import "base:runtime" +import "core:slice" -read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) { - return _read_directory(f, n, allocator) +@(require_results) +read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) { + if f == nil { + return nil, .Invalid_File + } + + n := n + size := n + if n <= 0 { + n = -1 + size = 100 + } + + TEMP_ALLOCATOR_GUARD() + + it := read_directory_iterator_create(f) or_return + defer _read_directory_iterator_destroy(&it) + + dfi := make([dynamic]File_Info, 0, size, temp_allocator()) + defer if err != nil { + for fi in dfi { + file_info_delete(fi, allocator) + } + } + + for fi, index in read_directory_iterator(&it) { + if n > 0 && index == n { + break + } + append(&dfi, file_info_clone(fi, allocator) or_return) + } + + return slice.clone(dfi[:], allocator) } + +@(require_results) read_all_directory :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) { return read_directory(f, -1, allocator) } +@(require_results) read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) { f := open(path) or_return defer close(f) return read_directory(f, n, allocator) } +@(require_results) read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) { return read_directory_by_path(path, -1, allocator) -} \ No newline at end of file +} + + +Read_Directory_Iterator :: struct { + f: ^File, + impl: Read_Directory_Iterator_Impl, +} + + +@(require_results) +read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) { + return _read_directory_iterator_create(f) +} + +read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { + _read_directory_iterator_destroy(it) +} + + +// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone` +@(require_results) +read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) { + return _read_directory_iterator(it) +} diff --git a/core/os/os2/dir_linux.odin b/core/os/os2/dir_linux.odin index b3d4aed0b..d4f62e213 100644 --- a/core/os/os2/dir_linux.odin +++ b/core/os/os2/dir_linux.odin @@ -1,8 +1,20 @@ +//+private package os2 -import "base:runtime" +Read_Directory_Iterator_Impl :: struct { -@(private) -_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) { +} + + +@(require_results) +_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) { return } + +@(require_results) +_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) { + return {}, nil +} + +_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { +} diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin index 36246267d..99dbe3c17 100644 --- a/core/os/os2/dir_windows.odin +++ b/core/os/os2/dir_windows.odin @@ -1,67 +1,104 @@ +//+private package os2 import "base:runtime" import "core:time" import win32 "core:sys/windows" -@(private) -_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) { - find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { - // Ignore "." and ".." - if d.cFileName[0] == '.' && d.cFileName[1] == 0 { - return - } - if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { - return - } - path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return - fi.fullpath = path - fi.name = basename(path) - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) +@(private="file") +find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { + // Ignore "." and ".." + if d.cFileName[0] == '.' && d.cFileName[1] == 0 { + return + } + if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { + return + } + path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return + fi.fullpath = path + fi.name = basename(path) + fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { - fi.mode |= 0o444 - } else { - fi.mode |= 0o666 - } + if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { + fi.mode |= 0o444 + } else { + fi.mode |= 0o666 + } - is_sym := false - if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 { - is_sym = false - } else { - is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT - } + is_sym := false + if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 { + is_sym = false + } else { + is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT + } - if is_sym { - fi.type = .Symlink - } else if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - fi.type = .Directory - fi.mode |= 0o111 - } + if is_sym { + fi.type = .Symlink + } else if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + fi.type = .Directory + fi.mode |= 0o111 + } - fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) - fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) - fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) + fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) + fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) + fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) + return +} + +Read_Directory_Iterator_Impl :: struct { + find_data: win32.WIN32_FIND_DATAW, + find_handle: win32.HANDLE, + path: string, + prev_fi: File_Info, + no_more_files: bool, + index: int, +} + + +@(require_results) +_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) { + if it.f == nil { + return + } + if it.impl.no_more_files { return } - if f == nil { - return nil, .Invalid_File + + err: Error + fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator()) + if err != nil { + return + } + if fi.name != "" { + file_info_delete(it.impl.prev_fi, file_allocator()) + it.impl.prev_fi = fi + ok = true + index = it.impl.index + it.impl.index += 1 } - TEMP_ALLOCATOR_GUARD() + if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) { + e := _get_platform_error() + if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) { + it.impl.no_more_files = true + } + it.impl.no_more_files = true + return + } + return +} +@(require_results) +_read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator, err: Error) { + if f == nil { + return + } impl := (^File_Impl)(f.impl) if !is_directory(impl.name) { - return nil, .Invalid_Dir - } - - n := n - size := n - if n <= 0 { - n = -1 - size = 100 + err = .Invalid_Dir + return } wpath: []u16 @@ -73,46 +110,31 @@ _read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (file wpath = impl.wname[:i] } + TEMP_ALLOCATOR_GUARD() - wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) + wpath_search := make([]u16, len(wpath)+3, temp_allocator()) copy(wpath_search, wpath) wpath_search[len(wpath)+0] = '\\' wpath_search[len(wpath)+1] = '*' wpath_search[len(wpath)+2] = 0 - find_data := &win32.WIN32_FIND_DATAW{} - find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data) - if find_handle == win32.INVALID_HANDLE_VALUE { - return nil, _get_platform_error() + it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data) + if it.impl.find_handle == win32.INVALID_HANDLE_VALUE { + err = _get_platform_error() + return } - defer win32.FindClose(find_handle) - - path := _cleanpath_from_buf(wpath, temp_allocator()) or_return - - dfi := make([dynamic]File_Info, 0, size, allocator) defer if err != nil { - for fi in dfi { - file_info_delete(fi, allocator) - } - delete(dfi) - } - for n != 0 { - fi: File_Info - fi = find_data_to_file_info(path, find_data, allocator) or_return - if fi.name != "" { - append(&dfi, fi) - n -= 1 - } - - if !win32.FindNextFileW(find_handle, find_data) { - e := _get_platform_error() - if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) { - break - } - return dfi[:], e - } + win32.FindClose(it.impl.find_handle) } - return dfi[:], nil + it.impl.path = _cleanpath_from_buf(wpath, file_allocator()) or_return + return } +_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { + if it.f == nil { + return + } + file_info_delete(it.impl.prev_fi, file_allocator()) + win32.FindClose(it.impl.find_handle) +} \ No newline at end of file diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index e67045b38..a324e7189 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -1,7 +1,9 @@ package os2 -import "core:time" import "base:runtime" +import "core:path/filepath" +import "core:strings" +import "core:time" Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) @@ -19,6 +21,14 @@ File_Info :: struct { access_time: time.Time, } +@(require_results) +file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) { + cloned = fi + cloned.fullpath = strings.clone(fi.fullpath) or_return + cloned.name = filepath.base(cloned.fullpath) + return +} + file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) { for i := len(infos)-1; i >= 0; i -= 1 { file_info_delete(infos[i], allocator)