@@ -52,55 +52,71 @@ in stringBuilder.GetPinnableReference(true),
5252 // NB: This method assumes backslashes have been normalized to forward slashes
5353 // NB: This method operates on the actual file system
5454 private bool FileExistsCaseInsensitive ( ReadOnlySpan < char > filePath , ref ValueStringBuilder stringBuilder )
55- {
56- Debug . Assert ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ) ;
57-
58- var pathString = filePath . ToString ( ) ;
59- if ( _underlyingFileSystem . File . Exists ( pathString ) )
60- return true ;
55+ {
56+ Debug . Assert ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ) ;
6157
62- var directory = _underlyingFileSystem . Path . GetDirectoryName ( pathString ) ;
63- var fileName = _underlyingFileSystem . Path . GetFileName ( pathString ) ;
58+ var pathString = filePath . ToString ( ) ;
59+ if ( _underlyingFileSystem . File . Exists ( pathString ) )
60+ return true ;
6461
65- if ( string . IsNullOrEmpty ( directory ) || string . IsNullOrEmpty ( fileName ) )
66- return false ;
62+ var segments = pathString . Split ( '/' ) ;
63+ var currentPath = segments [ 0 ] . Length == 0 ? "/" : segments [ 0 ] ;
6764
68- if ( ! _underlyingFileSystem . Directory . Exists ( directory ) )
69- {
70- if ( ! FileExistsCaseInsensitive ( directory . AsSpan ( ) , ref stringBuilder ) )
71- return false ;
65+ var lastSegmentIndex = segments . Length - 1 ;
66+ while ( lastSegmentIndex > 0 && string . IsNullOrEmpty ( segments [ lastSegmentIndex ] ) )
67+ lastSegmentIndex -- ;
7268
73- directory = stringBuilder . AsSpan ( ) . ToString ( ) ;
74- }
69+ for ( var i = 1 ; i < segments . Length ; i ++ )
70+ {
71+ var segment = segments [ i ] ;
72+ if ( string . IsNullOrEmpty ( segment ) )
73+ continue ;
7574
76- var files = _underlyingFileSystem . Directory . GetFiles ( directory ) ;
77- var directories = _underlyingFileSystem . Directory . GetDirectories ( directory ) ;
75+ var isLastSegment = i == lastSegmentIndex ;
7876
79- foreach ( var file in files )
77+ // Guard: if currentPath doesn't exist as a directory, bail out cheaply
78+ if ( ! _underlyingFileSystem . Directory . Exists ( currentPath ) )
79+ return false ;
80+
81+ if ( isLastSegment )
8082 {
81- var name = _underlyingFileSystem . Path . GetFileName ( file ) ;
82- if ( name . Equals ( fileName , StringComparison . OrdinalIgnoreCase ) )
83+ // Single IO call instead of GetFiles + GetDirectories
84+ var entries = _underlyingFileSystem . Directory . GetFileSystemEntries ( currentPath ) ;
85+ foreach ( var entry in entries )
8386 {
84- stringBuilder . Length = 0 ;
85- stringBuilder . Append ( file ) ;
86- return true ;
87+ var name = _underlyingFileSystem . Path . GetFileName ( entry ) ;
88+ if ( name . Equals ( segment , StringComparison . OrdinalIgnoreCase ) )
89+ {
90+ stringBuilder . Length = 0 ;
91+ stringBuilder . Append ( entry ) ;
92+ return true ;
93+ }
8794 }
95+ return false ;
8896 }
8997
90- foreach ( var dir in directories )
98+ // Intermediate segment: resolve case-insensitively
99+ var subDirs = _underlyingFileSystem . Directory . GetDirectories ( currentPath ) ;
100+ string ? resolved = null ;
101+ foreach ( var dir in subDirs )
91102 {
92103 var name = _underlyingFileSystem . Path . GetFileName ( dir ) ;
93- if ( name . Equals ( fileName , StringComparison . OrdinalIgnoreCase ) )
104+ if ( name . Equals ( segment , StringComparison . OrdinalIgnoreCase ) )
94105 {
95- stringBuilder . Length = 0 ;
96- stringBuilder . Append ( dir ) ;
97- return true ;
106+ resolved = dir ;
107+ break ;
98108 }
99109 }
100110
101- return false ;
111+ if ( resolved is null )
112+ return false ;
113+
114+ currentPath = resolved ;
102115 }
103-
116+
117+ return false ;
118+ }
119+
104120 private bool IsPathFullyQualified_Exists ( ReadOnlySpan < char > path )
105121 {
106122 // This is really tricky, because under Windows "/" or "\" do NOT
0 commit comments