Skip to content

Commit 803b033

Browse files
committed
Foundation: avoid unnecessary IO on directory enumeration
Prefer to use the `hasDirectoryPath` ivar on the URL which should be properly populated when populating the stack. The one special case is when we first initialise the enumerator, where we can assume that we are given a directory path.
1 parent 4eacf88 commit 803b033

File tree

1 file changed

+20
-14
lines changed

1 file changed

+20
-14
lines changed

Sources/Foundation/FileManager+Win32.swift

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -929,15 +929,14 @@ extension FileManager {
929929
var _options : FileManager.DirectoryEnumerationOptions
930930
var _errorHandler : ((URL, Error) -> Bool)?
931931
var _stack: [URL]
932-
var _lastReturned: URL
933-
var _rootDepth : Int
932+
var _lastReturned: URL?
933+
var _root: URL
934934

935935
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) {
936936
_options = options
937937
_errorHandler = errorHandler
938938
_stack = []
939-
_rootDepth = url.pathComponents.count
940-
_lastReturned = url
939+
_root = url
941940
}
942941

943942
override func nextObject() -> Any? {
@@ -955,17 +954,22 @@ extension FileManager {
955954
return nil
956955
}
957956

958-
// If we most recently returned a directory, decend into it
959-
guard let attrs = try? FileManager.default.windowsFileAttributes(atPath: _lastReturned.path) else {
960-
guard let handler = _errorHandler,
961-
handler(_lastReturned, _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [_lastReturned.path]))
962-
else { return nil }
963-
return firstValidItem()
957+
if _lastReturned == nil {
958+
guard let attrs = try? FileManager.default.windowsFileAttributes(atPath: _root.path) else {
959+
guard let handler = _errorHandler else { return nil }
960+
if !handler(_root, _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [_root.path])) {
961+
return nil
962+
}
963+
return firstValidItem()
964+
}
965+
966+
let isDirectory = attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY && attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != FILE_ATTRIBUTE_REPARSE_POINT
967+
_lastReturned = URL(fileURLWithPath: _root.path, isDirectory: isDirectory)
964968
}
965969

966-
let isDir = attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY &&
967-
attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT == 0
968-
if isDir && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) {
970+
guard let _lastReturned else { return firstValidItem() }
971+
972+
if _lastReturned.hasDirectoryPath && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) {
969973
var ffd = WIN32_FIND_DATAW()
970974
let capacity = MemoryLayout.size(ofValue: ffd.cFileName)
971975

@@ -991,11 +995,13 @@ extension FileManager {
991995
_stack.append(_lastReturned.appendingPathComponent(file, isDirectory: isDirectory))
992996
} while FindNextFileW(handle, &ffd)
993997
}
998+
994999
return firstValidItem()
9951000
}
9961001

9971002
override var level: Int {
998-
return _lastReturned.pathComponents.count - _rootDepth
1003+
guard let _lastReturned else { return 0 }
1004+
return _lastReturned.pathComponents.count - _root.pathComponents.count
9991005
}
10001006

10011007
override func skipDescendants() {

0 commit comments

Comments
 (0)