Skip to content

Commit

Permalink
Make final API review changes to file enumeration (dotnet/corefx#27318)
Browse files Browse the repository at this point in the history
* Make final API review changes to file enumeration

Tweak stack array initialization to just zero the first element in matcher algorithm.

cc: @danmosemsft @terrajobst

* Don't null out the native name to track conversion


Commit migrated from dotnet/corefx@62878f3
  • Loading branch information
JeremyKuhne authored Feb 22, 2018
1 parent ef1cab8 commit c603407
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public enum SearchOption
public enum MatchType
{
Simple,
Dos
Win32
}
public enum MatchCasing
{
Expand Down Expand Up @@ -260,6 +260,7 @@ public ref struct FileSystemEntry
public DateTimeOffset LastAccessTimeUtc { get { throw null; } }
public DateTimeOffset LastWriteTimeUtc { get { throw null; } }
public bool IsDirectory { get { throw null; } }
public bool IsHidden { get { throw null; } }
public FileSystemInfo ToFileSystemInfo() { throw null; }
public string ToSpecifiedFullPath() { throw null; }
public string ToFullPath() { throw null; }
Expand Down Expand Up @@ -297,8 +298,8 @@ public FileSystemEnumerable(string directory, FindTransform transform, Enumerati
}
public static class FileSystemName
{
public static string TranslateDosExpression(string expression) { throw null; }
public static bool MatchesDosExpression(ReadOnlySpan<char> expression, ReadOnlySpan<char> name, bool ignoreCase = true) { throw null; }
public static string TranslateWin32Expression(string expression) { throw null; }
public static bool MatchesWin32Expression(ReadOnlySpan<char> expression, ReadOnlySpan<char> name, bool ignoreCase = true) { throw null; }
public static bool MatchesSimpleExpression(ReadOnlySpan<char> expression, ReadOnlySpan<char> name, bool ignoreCase = true) { throw null; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,13 @@ public ReadOnlySpan<char> FileName
{
get
{
if (_directoryEntry.Name != null)
if (_directoryEntry.NameLength != 0 && _fileName.Length == 0)
{
fixed (char* c = _fileNameBuffer)
{
Span<char> buffer = new Span<char>(c, FileNameBufferSize);
_fileName = _directoryEntry.GetName(buffer);
}
_directoryEntry.Name = null;
}

return _fileName;
Expand Down Expand Up @@ -129,6 +128,7 @@ public FileAttributes Attributes
public DateTimeOffset LastAccessTimeUtc => _status.GetLastAccessTime(FullPath, continueOnError: true);
public DateTimeOffset LastWriteTimeUtc => _status.GetLastWriteTime(FullPath, continueOnError: true);
public bool IsDirectory => _status.InitiallyDirectory;
public bool IsHidden => _directoryEntry.Name[0] == '.';

public FileSystemInfo ToFileSystemInfo()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ internal static void Initialize(
/// </summary>
public bool IsDirectory => (Attributes & FileAttributes.Directory) != 0;

/// <summary>
/// Returns true if the file has the hidden attribute.
/// </summary>
public bool IsHidden => (Attributes & FileAttributes.Hidden) != 0;

public FileSystemInfo ToFileSystemInfo()
=> FileSystemInfo.Create(PathHelpers.CombineNoChecks(Directory, FileName), ref this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal static void NormalizeInputs(ref string directory, ref string expression

switch (options.MatchType)
{
case MatchType.Dos:
case MatchType.Win32:
if (string.IsNullOrEmpty(expression) || expression == "." || expression == "*.*")
{
// Historically we always treated "." as "*"
Expand All @@ -57,7 +57,7 @@ internal static void NormalizeInputs(ref string directory, ref string expression
}

// Need to convert the expression to match Win32 behavior
expression = FileSystemName.TranslateDosExpression(expression);
expression = FileSystemName.TranslateWin32Expression(expression);
}
break;
case MatchType.Simple:
Expand All @@ -76,8 +76,8 @@ private static bool MatchesPattern(string expression, ReadOnlySpan<char> name, E
{
case MatchType.Simple:
return FileSystemName.MatchesSimpleExpression(expression, name, ignoreCase);
case MatchType.Dos:
return FileSystemName.MatchesDosExpression(expression, name, ignoreCase);
case MatchType.Win32:
return FileSystemName.MatchesWin32Expression(expression, name, ignoreCase);
default:
throw new ArgumentOutOfRangeException(nameof(options));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class FileSystemName
/// Change '*' and '?' to '&lt;', '&gt;' and '"' to match Win32 behavior. For compatibility, Windows
/// changes some wildcards to provide a closer match to historical DOS 8.3 filename matching.
/// </summary>
public static string TranslateDosExpression(string expression)
public static string TranslateWin32Expression(string expression)
{
if (string.IsNullOrEmpty(expression) || expression == "*" || expression == "*.*")
return "*";
Expand Down Expand Up @@ -82,9 +82,9 @@ public static string TranslateDosExpression(string expression)
/// of RtlIsNameInExpression, which defines the rules for matching DOS wildcards ('*', '?', '&lt;', '&gt;', '"').
///
/// Like PatternMatcher, matching will not line up with Win32 behavior unless you transform the expression
/// using <see cref="TranslateDosExpression(string)"/>
/// using <see cref="TranslateWin32Expression(string)"/>
/// </remarks>
public static bool MatchesDosExpression(ReadOnlySpan<char> expression, ReadOnlySpan<char> name, bool ignoreCase = true)
public static bool MatchesWin32Expression(ReadOnlySpan<char> expression, ReadOnlySpan<char> name, bool ignoreCase = true)
{
return MatchPattern(expression, name, ignoreCase, useExtendedWildcards: true);
}
Expand Down Expand Up @@ -141,7 +141,7 @@ private static bool MatchPattern(ReadOnlySpan<char> expression, ReadOnlySpan<cha
Span<int> temp = stackalloc int[0];
Span<int> currentMatches = stackalloc int[16];
Span<int> priorMatches = stackalloc int[16];
priorMatches.Clear();
priorMatches[0] = 0;

int maxState = expression.Length * 2;
int currentState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ public class EnumerationOptions
/// explicitly specify EnumerationOptions.
/// </summary>
internal static EnumerationOptions Compatible { get; } = new EnumerationOptions
{ MatchType = MatchType.Dos, AttributesToSkip = 0, IgnoreInaccessible = false };
{ MatchType = MatchType.Win32, AttributesToSkip = 0, IgnoreInaccessible = false };

private static EnumerationOptions CompatibleRecursive { get; } = new EnumerationOptions
{ RecurseSubdirectories = true, MatchType = MatchType.Dos, AttributesToSkip = 0, IgnoreInaccessible = false };
{ RecurseSubdirectories = true, MatchType = MatchType.Win32, AttributesToSkip = 0, IgnoreInaccessible = false };

/// <summary>
/// Internal singleton for default options.
Expand Down
4 changes: 2 additions & 2 deletions src/libraries/System.IO.FileSystem/src/System/IO/MatchType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public enum MatchType
Simple,

/// <summary>
/// Match using DOS style matching semantics. '*', '?', '&lt;', '&gt;', and '"'
/// Match using Win32 DOS style matching semantics. '*', '?', '&lt;', '&gt;', and '"'
/// are all considered wildcards.
/// </summary>
Dos
Win32
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.IO.Enumeration;
using Xunit;

Expand Down Expand Up @@ -111,5 +112,30 @@ public void DirectoryAttributesAreExpected()
Assert.False(enumerator.MoveNext());
}
}

[Fact]
public void IsHiddenAttribute()
{
DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, GetTestFileName()));

// Put a period in front to make it hidden on Unix
FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "." + GetTestFileName()));

fileOne.Create().Dispose();
fileTwo.Create().Dispose();
if (PlatformDetection.IsWindows)
fileTwo.Attributes = fileTwo.Attributes | FileAttributes.Hidden;

IEnumerable<string> enumerable = new FileSystemEnumerable<string>(
testDirectory.FullName,
(ref FileSystemEntry entry) => entry.ToFullPath(),
new EnumerationOptions() { AttributesToSkip = 0 })
{
ShouldIncludePredicate = (ref FileSystemEntry entry) => entry.IsHidden
};

Assert.Equal(new string[] { fileTwo.FullName }, enumerable);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void GetFiles_WildcardPatternIsTranslated(string pattern)
string[] results = GetFiles(testDirectory.FullName, pattern);
FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, results);

results = GetFiles(testDirectory.FullName, pattern, new EnumerationOptions { MatchType = MatchType.Dos });
results = GetFiles(testDirectory.FullName, pattern, new EnumerationOptions { MatchType = MatchType.Win32 });
FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName }, results);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

namespace System.IO.Tests
{
public class DosMatcherTests
public class Win32MatcherTests
{
[Theory, MemberData(nameof(DosMatchData)), MemberData(nameof(EscapedDosMatchData))]
public static void DosMatch(string expression, string name, bool ignoreCase, bool expected)
[Theory, MemberData(nameof(Win32MatchData)), MemberData(nameof(EscapedWin32MatchData))]
public static void Win32Match(string expression, string name, bool ignoreCase, bool expected)
{
Assert.Equal(expected, FileSystemName.MatchesDosExpression(expression, name.AsSpan(), ignoreCase));
Assert.Equal(expected, FileSystemName.MatchesWin32Expression(expression, name.AsSpan(), ignoreCase));
}

public static TheoryData<string, string, bool, bool> EscapedDosMatchData => new TheoryData<string, string, bool, bool>
public static TheoryData<string, string, bool, bool> EscapedWin32MatchData => new TheoryData<string, string, bool, bool>
{
// Trailing escape matches as it is considered "invisible"
{ "\\", "\\", false, true },
Expand Down Expand Up @@ -43,7 +43,7 @@ public static void DosMatch(string expression, string name, bool ignoreCase, boo
{ "\\\"", "\"", true, true },
};

public static TheoryData<string, string, bool, bool> DosMatchData => new TheoryData<string, string, bool, bool>
public static TheoryData<string, string, bool, bool> Win32MatchData => new TheoryData<string, string, bool, bool>
{
{ null, "", false, false },
{ null, "", true, false },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<Compile Include="Enumeration\ConstructionTests.netcoreapp.cs" />
<Compile Include="Enumeration\SpecialDirectoryTests.netcoreapp.cs" />
<Compile Include="Enumeration\SkipAttributeTests.netcoreapp.cs" />
<Compile Include="Enumeration\DosMatcherTests.netcoreapp.cs" />
<Compile Include="Enumeration\Win32MatcherTests.netcoreapp.cs" />
<Compile Include="Enumeration\MatchCasingTests.netcoreapp.cs" />
<Compile Include="Enumeration\TrimmedPaths.netcoreapp.cs" />
<Compile Include="Enumeration\ErrorHandlingTests.netcoreapp.cs" />
Expand Down

0 comments on commit c603407

Please sign in to comment.