{
public static partial class Path
{
- public static readonly char DirectorySeparatorChar = '/';
- public static readonly char VolumeSeparatorChar = '/';
- public static readonly char PathSeparator = ':';
-
- private const string DirectorySeparatorCharAsString = "/";
-
public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' };
- internal static readonly int MaxPath = Interop.Sys.MaxPath;
- private static readonly int MaxLongPath = MaxPath;
+ public static char[] GetInvalidPathChars() => new char[] { '\0' };
+
+ internal static int MaxPath => Interop.Sys.MaxPath;
private static readonly bool s_isMac = Interop.Sys.GetUnixName() == "OSX";
Debug.Assert(collapsedString.Length < path.Length || collapsedString.ToString() == path,
"Either we've removed characters, or the string should be unmodified from the input path.");
- if (collapsedString.Length > MaxPath)
+ if (collapsedString.Length > Interop.Sys.MaxPath)
{
throw new PathTooLongException(SR.IO_PathTooLong);
}
- string result = collapsedString.Length == 0 ? DirectorySeparatorCharAsString : collapsedString;
+ string result = collapsedString.Length == 0 ? PathInternal.DirectorySeparatorCharAsString : collapsedString;
return result;
}
}
}
- if (++componentCharCount > PathInternal.MaxComponentLength)
+ if (++componentCharCount > Interop.Sys.MaxName)
{
throw new PathTooLongException(SR.IO_PathTooLong);
}
// Normalize the directory separator if needed
- if (c != Path.DirectorySeparatorChar && c == Path.AltDirectorySeparatorChar)
+ if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar)
{
- c = Path.DirectorySeparatorChar;
+ c = PathInternal.DirectorySeparatorChar;
flippedSeparator = true;
}
return
string.IsNullOrEmpty(path) ? DefaultTempPath :
PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? path :
- path + DirectorySeparatorChar;
+ path + PathInternal.DirectorySeparatorChar;
}
public static string GetTempFileName()
return false;
PathInternal.CheckInvalidPathChars(path);
- return path.Length > 0 && path[0] == DirectorySeparatorChar;
+ return path.Length > 0 && path[0] == PathInternal.DirectorySeparatorChar;
}
public static string GetPathRoot(string path)
{
if (path == null) return null;
- return IsPathRooted(path) ? DirectorySeparatorCharAsString : String.Empty;
+ return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : String.Empty;
}
private static unsafe void GetCryptoRandomBytes(byte* bytes, int byteCount)
{
-#if FEATURE_CORECLR
// We want to avoid dependencies on the Crypto library when compiling in CoreCLR. This
// will use the existing PAL implementation.
byte[] buffer = new byte[KeyLength];
Microsoft.Win32.Win32Native.Random(bStrong: true, buffer: buffer, length: KeyLength);
Runtime.InteropServices.Marshal.Copy(buffer, 0, (IntPtr)bytes, KeyLength);
-#else
- if (s_isMac)
- {
- GetCryptoRandomBytesApple(bytes, byteCount);
- }
- else
- {
- GetCryptoRandomBytesOpenSsl(bytes, byteCount);
- }
-#endif
- }
-
-#if !FEATURE_CORECLR
- private static unsafe void GetCryptoRandomBytesApple(byte* bytes, int byteCount)
- {
- Debug.Assert(bytes != null);
- Debug.Assert(byteCount >= 0);
-
- if (Interop.CommonCrypto.CCRandomGenerateBytes(bytes, byteCount) != 0)
- {
- throw new InvalidOperationException(SR.InvalidOperation_Cryptography);
- }
- }
-
- private static unsafe void GetCryptoRandomBytesOpenSsl(byte* bytes, int byteCount)
- {
- Debug.Assert(bytes != null);
- Debug.Assert(byteCount >= 0);
-
- if (!Interop.Crypto.GetRandomBytes(bytes, byteCount))
- {
- throw new InvalidOperationException(SR.InvalidOperation_Cryptography);
- }
}
-#endif
/// <summary>Gets whether the system is case-sensitive.</summary>
internal static bool IsCaseSensitive { get { return !s_isMac; } }
{
public static partial class Path
{
- public static readonly char DirectorySeparatorChar = '\\';
- public static readonly char VolumeSeparatorChar = ':';
- public static readonly char PathSeparator = ';';
-
- private const string DirectorySeparatorCharAsString = "\\";
-
public static char[] GetInvalidFileNameChars() => new char[]
- {
+ {
'\"', '<', '>', '|', '\0',
(char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
(char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
(char)31, ':', '*', '?', '\\', '/'
};
+ public static char[] GetInvalidPathChars() => new char[]
+ {
+ '|', '\0',
+ (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
+ (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
+ (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
+ (char)31
+ };
+
// The max total path is 260, and the max individual component length is 255.
// For example, D:\<256 char file name> isn't legal, even though it's under 260 chars.
- internal static readonly int MaxPath = 260;
- internal static readonly int MaxLongPath = short.MaxValue;
+ internal const int MaxPath = 260;
// Expands the given path to a fully qualified path.
public static string GetFullPath(string path)
// Move past the colon
startIndex += 2;
- if ((path.Length > 0 && path[0] == VolumeSeparatorChar)
- || (path.Length >= startIndex && path[startIndex - 1] == VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2]))
- || (path.Length > startIndex && path.IndexOf(VolumeSeparatorChar, startIndex) != -1))
+ if ((path.Length > 0 && path[0] == PathInternal.VolumeSeparatorChar)
+ || (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2]))
+ || (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1))
{
throw new NotSupportedException(SR.Argument_PathFormatNotSupported);
}
int length = path.Length;
if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])) ||
- (length >= 2 && path[1] == VolumeSeparatorChar))
+ (length >= 2 && path[1] == PathInternal.VolumeSeparatorChar))
return true;
}
return false;
// but they will handle most string operations.
public static partial class Path
{
- // Platform specific alternate directory separator character.
- // There is only one directory separator char on Unix, which is the same
- // as the alternate separator on Windows, so same definition is used for both.
- public static readonly char AltDirectorySeparatorChar = '/';
+ // Public static readonly variant of the separators. The Path implementation itself is using
+ // internal const variant of the separators for better performance.
+ public static readonly char DirectorySeparatorChar = PathInternal.DirectorySeparatorChar;
+ public static readonly char AltDirectorySeparatorChar = PathInternal.AltDirectorySeparatorChar;
+ public static readonly char VolumeSeparatorChar = PathInternal.VolumeSeparatorChar;
+ public static readonly char PathSeparator = PathInternal.PathSeparator;
// For generating random file names
// 8 random bytes provides 12 chars in our encoding for the 8.3 name.
return null;
}
- public static char[] GetInvalidPathChars()
- {
- return PathInternal.GetInvalidPathChars();
- }
-
// Returns the extension of the given path. The returned value includes the
// period (".") character of the extension except when you have a terminal period when you get string.Empty, such as ".exe" or
// ".cpp". The returned value is null if the given path is
char ch = finalPath[finalPath.Length - 1];
if (!PathInternal.IsDirectoryOrVolumeSeparator(ch))
{
- finalPath.Append(DirectorySeparatorChar);
+ finalPath.Append(PathInternal.DirectorySeparatorChar);
}
finalPath.Append(paths[i]);
char ch = path1[path1.Length - 1];
return PathInternal.IsDirectoryOrVolumeSeparator(ch) ?
path1 + path2 :
- path1 + DirectorySeparatorCharAsString + path2;
+ path1 + PathInternal.DirectorySeparatorCharAsString + path2;
}
private static string CombineNoChecks(string path1, string path2, string path3)
}
else if (hasSep1)
{
- return path1 + path2 + DirectorySeparatorCharAsString + path3;
+ return path1 + path2 + PathInternal.DirectorySeparatorCharAsString + path3;
}
else if (hasSep2)
{
- return path1 + DirectorySeparatorCharAsString + path2 + path3;
+ return path1 + PathInternal.DirectorySeparatorCharAsString + path2 + path3;
}
else
{
// a params string[]. Instead, try to use a cached StringBuilder.
StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + 2);
sb.Append(path1)
- .Append(DirectorySeparatorChar)
+ .Append(PathInternal.DirectorySeparatorChar)
.Append(path2)
- .Append(DirectorySeparatorChar)
+ .Append(PathInternal.DirectorySeparatorChar)
.Append(path3);
return StringBuilderCache.GetStringAndRelease(sb);
}
sb.Append(path1);
if (!hasSep1)
{
- sb.Append(DirectorySeparatorChar);
+ sb.Append(PathInternal.DirectorySeparatorChar);
}
sb.Append(path2);
if (!hasSep2)
{
- sb.Append(DirectorySeparatorChar);
+ sb.Append(PathInternal.DirectorySeparatorChar);
}
sb.Append(path3);
if (!hasSep3)
{
- sb.Append(DirectorySeparatorChar);
+ sb.Append(PathInternal.DirectorySeparatorChar);
}
sb.Append(path4);
/// <summary>
/// Wrapper to help with path normalization.
/// </summary>
- unsafe internal class PathHelper
+ internal class PathHelper
{
// Can't be over 8.3 and be a short name
private const int MaxShortName = 12;
return !PathInternal.IsDevice(ref buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\';
}
- private static void GetFullPathName(string path, ref StringBuffer fullPath)
+ private static unsafe void GetFullPathName(string path, ref StringBuffer fullPath)
{
// If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
// it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
/// <summary>Contains internal path helpers that are shared between many projects.</summary>
internal static partial class PathInternal
{
+ internal const char DirectorySeparatorChar = '/';
+ internal const char AltDirectorySeparatorChar = '/';
+ internal const char VolumeSeparatorChar = '/';
+ internal const char PathSeparator = ':';
+
+ internal const string DirectorySeparatorCharAsString = "/";
+
// There is only one invalid path character in Unix
private const char InvalidPathChar = '\0';
- internal static char[] GetInvalidPathChars() => new char[] { InvalidPathChar };
-
- internal static readonly int MaxComponentLength = Interop.Sys.MaxName;
internal const string ParentDirectoryPrefix = @"../";
{
// The alternate directory separator char is the same as the directory separator,
// so we only need to check one.
- Debug.Assert(Path.DirectorySeparatorChar == Path.AltDirectorySeparatorChar);
- return c == Path.DirectorySeparatorChar;
+ Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar);
+ return c == DirectorySeparatorChar;
}
/// <summary>
{
// The directory separator, volume separator, and the alternate directory
// separator should be the same on Unix, so we only need to check one.
- Debug.Assert(Path.DirectorySeparatorChar == Path.AltDirectorySeparatorChar);
- Debug.Assert(Path.DirectorySeparatorChar == Path.VolumeSeparatorChar);
- return ch == Path.DirectorySeparatorChar;
+ Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar);
+ Debug.Assert(DirectorySeparatorChar == VolumeSeparatorChar);
+ return ch == DirectorySeparatorChar;
}
internal static bool HasInvalidVolumeSeparator(string path)
// The only way to specify a fixed path that doesn't begin with two slashes
// is the drive, colon, slash format- i.e. C:\
return !((path.Length >= 3)
- && (path[1] == Path.VolumeSeparatorChar)
+ && (path[1] == VolumeSeparatorChar)
&& IsDirectorySeparator(path[2]));
}
}
// Local and Global MS-DOS Device Names
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff554302.aspx
+ internal const char DirectorySeparatorChar = '\\';
+ internal const char AltDirectorySeparatorChar = '/';
+ internal const char VolumeSeparatorChar = ':';
+ internal const char PathSeparator = ';';
+
+ internal const string DirectorySeparatorCharAsString = "\\";
+
internal const string ExtendedPathPrefix = @"\\?\";
internal const string UncPathPrefix = @"\\";
internal const string UncExtendedPrefixToInsert = @"?\UNC\";
internal const int UncExtendedPrefixLength = 8;
internal const int MaxComponentLength = 255;
- internal static char[] GetInvalidPathChars() => new char[]
- {
- '|', '\0',
- (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
- (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
- (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
- (char)31
- };
-
- // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression
- // https://msdn.microsoft.com/en-us/library/ff469270.aspx
- private static readonly char[] s_wildcardChars =
- {
- '\"', '<', '>', '*', '?'
- };
-
/// <summary>
/// Returns true if the given character is a valid drive letter
/// </summary>
for (int i = 0; i < path.Length; i++)
{
char c = path[i];
-
- if (c <= '\u001f' || c == '|')
+ if (c <= '|') // fast path for common case - '|' is highest illegal character
{
- return true;
+ if (c <= '\u001f' || c == '|')
+ {
+ return true;
+ }
}
}
/// <summary>
/// Check for known wildcard characters. '*' and '?' are the most common ones.
/// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal unsafe static bool HasWildCardCharacters(string path)
+ internal static bool HasWildCardCharacters(string path)
{
// Question mark is part of dos device syntax so we have to skip if we are
int startIndex = IsDevice(path) ? ExtendedPathPrefix.Length : 0;
- return path.IndexOfAny(s_wildcardChars, startIndex) >= 0;
+ // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression
+ // https://msdn.microsoft.com/en-us/library/ff469270.aspx
+ for (int i = startIndex; i < path.Length; i++)
+ {
+ char c = path[i];
+ if (c <= '?') // fast path for common case - '?' is highest wildcard character
+ {
+ if (c == '\"' || c == '<' || c == '>' || c == '*' || c == '?')
+ return true;
+ }
+ }
+
+ return false;
}
/// <summary>
while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) i++;
}
}
- else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == Path.VolumeSeparatorChar)
+ else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == VolumeSeparatorChar)
{
// Path is at least longer than where we expect a colon, and has a colon (\\?\A:, A:)
// If the colon is followed by a directory separator, move past it
// The only way to specify a fixed path that doesn't begin with two slashes
// is the drive, colon, slash format- i.e. C:\
return !((path.Length >= 3)
- && (path[1] == Path.VolumeSeparatorChar)
+ && (path[1] == VolumeSeparatorChar)
&& IsDirectorySeparator(path[2])
// To match old behavior we'll check the drive character for validity as the path is technically
// not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsDirectorySeparator(char c)
{
- return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar;
+ return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
}
/// <summary>
{
current = path[i];
if (IsDirectorySeparator(current)
- && (current != Path.DirectorySeparatorChar
+ && (current != DirectorySeparatorChar
// Check for sequential separators past the first position (we need to keep initial two for UNC/extended)
|| (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))))
{
if (IsDirectorySeparator(path[start]))
{
start++;
- builder.Append(Path.DirectorySeparatorChar);
+ builder.Append(DirectorySeparatorChar);
}
for (int i = start; i < path.Length; i++)
}
// Ensure it is the primary separator
- current = Path.DirectorySeparatorChar;
+ current = DirectorySeparatorChar;
}
builder.Append(current);
/// <param name="ch">The character to test.</param>
internal static bool IsDirectoryOrVolumeSeparator(char ch)
{
- return IsDirectorySeparator(ch) || Path.VolumeSeparatorChar == ch;
+ return IsDirectorySeparator(ch) || VolumeSeparatorChar == ch;
}
/// <summary>
int startIndex = IsExtended(path) ? ExtendedPathPrefix.Length : PathStartSkip(path);
// If we start with a colon
- if ((path.Length > startIndex && path[startIndex] == Path.VolumeSeparatorChar)
+ if ((path.Length > startIndex && path[startIndex] == VolumeSeparatorChar)
// Or have an invalid drive letter and colon
- || (path.Length >= startIndex + 2 && path[startIndex + 1] == Path.VolumeSeparatorChar && !IsValidDriveChar(path[startIndex]))
+ || (path.Length >= startIndex + 2 && path[startIndex + 1] == VolumeSeparatorChar && !IsValidDriveChar(path[startIndex]))
// Or have any colons beyond the drive colon
- || (path.Length > startIndex + 2 && path.IndexOf(Path.VolumeSeparatorChar, startIndex + 2) != -1))
+ || (path.Length > startIndex + 2 && path.IndexOf(VolumeSeparatorChar, startIndex + 2) != -1))
{
return true;
}
// See the LICENSE file in the project root for more information.
using System.Buffers;
+using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices
{
/// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to index outside of the buffer length.</exception>
public char this[int index]
{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
return _buffer[index];
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
/// of <paramref name="value"/> characters.
/// </exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="destination"/> is null.</exception>
- public unsafe void CopyTo(int bufferIndex, ref StringBuffer destination, int destinationIndex, int count)
+ public void CopyTo(int bufferIndex, ref StringBuffer destination, int destinationIndex, int count)
{
if (destinationIndex > destination._length) throw new ArgumentOutOfRangeException(nameof(destinationIndex));
if (bufferIndex >= _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
/// Copy contents from the specified string into the buffer at the given index. Start index must be within the current length of
/// the buffer, will grow as necessary.
/// </summary>
- public unsafe void CopyFrom(int bufferIndex, string source, int sourceIndex = 0, int count = -1)
+ public void CopyFrom(int bufferIndex, string source, int sourceIndex = 0, int count = -1)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (bufferIndex > _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
#if PLATFORM_UNIX
case Win32Native.ERROR_FILENAME_EXCED_RANGE:
// On Unix, length validation is done by CoreCLR's PAL after converting to utf-8
- throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", PathInternal.MaxComponentLength), "name");
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Interop.Sys.MaxName), "name");
#endif
case Win32Native.ERROR_INVALID_HANDLE:
if (name != null && errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE)
{
// On Unix, length validation is done by CoreCLR's PAL after converting to utf-8
- throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", PathInternal.MaxComponentLength), nameof(name));
+ throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", Interop.Sys.MaxName), nameof(name));
}
#endif