Przeglądaj źródła

Begin cleaning up `os2.read_directory`

gingerBill 1 rok temu
rodzic
commit
d4af7b86a7
4 zmienionych plików z 187 dodań i 84 usunięć
  1. 62 3
      core/os/os2/dir.odin
  2. 15 3
      core/os/os2/dir_linux.odin
  3. 99 77
      core/os/os2/dir_windows.odin
  4. 11 1
      core/os/os2/stat.odin

+ 62 - 3
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)
-}
+}
+
+
+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)
+}

+ 15 - 3
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) {
+}

+ 99 - 77
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)
-
-		if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
-			fi.mode |= 0o444
-		} else {
-			fi.mode |= 0o666
-		}
+@(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
+	}
 
-		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
+	}
+
+	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,
+}
 
-		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))
+@(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)
+}

+ 11 - 1
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)