From 803b03344d2d8732a50483ee20e890bfa9daec58 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sun, 11 Jun 2023 11:30:36 -0700 Subject: [PATCH] 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. --- Sources/Foundation/FileManager+Win32.swift | 34 +++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/Sources/Foundation/FileManager+Win32.swift b/Sources/Foundation/FileManager+Win32.swift index 8d486a1f28..99e938016b 100644 --- a/Sources/Foundation/FileManager+Win32.swift +++ b/Sources/Foundation/FileManager+Win32.swift @@ -929,15 +929,14 @@ extension FileManager { var _options : FileManager.DirectoryEnumerationOptions var _errorHandler : ((URL, Error) -> Bool)? var _stack: [URL] - var _lastReturned: URL - var _rootDepth : Int + var _lastReturned: URL? + var _root: URL init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) { _options = options _errorHandler = errorHandler _stack = [] - _rootDepth = url.pathComponents.count - _lastReturned = url + _root = url } override func nextObject() -> Any? { @@ -955,17 +954,22 @@ extension FileManager { return nil } - // If we most recently returned a directory, decend into it - guard let attrs = try? FileManager.default.windowsFileAttributes(atPath: _lastReturned.path) else { - guard let handler = _errorHandler, - handler(_lastReturned, _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [_lastReturned.path])) - else { return nil } - return firstValidItem() + if _lastReturned == nil { + guard let attrs = try? FileManager.default.windowsFileAttributes(atPath: _root.path) else { + guard let handler = _errorHandler else { return nil } + if !handler(_root, _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [_root.path])) { + return nil + } + return firstValidItem() + } + + let isDirectory = attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY && attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != FILE_ATTRIBUTE_REPARSE_POINT + _lastReturned = URL(fileURLWithPath: _root.path, isDirectory: isDirectory) } - let isDir = attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY && - attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT == 0 - if isDir && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) { + guard let _lastReturned else { return firstValidItem() } + + if _lastReturned.hasDirectoryPath && (level == 0 || !_options.contains(.skipsSubdirectoryDescendants)) { var ffd = WIN32_FIND_DATAW() let capacity = MemoryLayout.size(ofValue: ffd.cFileName) @@ -991,11 +995,13 @@ extension FileManager { _stack.append(_lastReturned.appendingPathComponent(file, isDirectory: isDirectory)) } while FindNextFileW(handle, &ffd) } + return firstValidItem() } override var level: Int { - return _lastReturned.pathComponents.count - _rootDepth + guard let _lastReturned else { return 0 } + return _lastReturned.pathComponents.count - _root.pathComponents.count } override func skipDescendants() {