Path normalization performance improvements
authorJan Kotas <jkotas@microsoft.com>
Wed, 4 Jan 2017 14:31:17 +0000 (06:31 -0800)
committerJan Kotas <jkotas@microsoft.com>
Wed, 4 Jan 2017 15:19:59 +0000 (07:19 -0800)
- Use const variant of path separators in the Path implementation
- Add fast paths for common cases to hot parsing routines
- Avoid redundant caches for MaxPath on Unix

Commit migrated from https://github.com/dotnet/coreclr/commit/d682dd337ccdc71e29a762d9bda8bf2cca93c122

src/coreclr/src/mscorlib/corefx/System/IO/Path.Unix.cs
src/coreclr/src/mscorlib/corefx/System/IO/Path.Windows.cs
src/coreclr/src/mscorlib/corefx/System/IO/Path.cs
src/coreclr/src/mscorlib/corefx/System/IO/PathHelper.Windows.cs
src/coreclr/src/mscorlib/corefx/System/IO/PathInternal.Unix.cs
src/coreclr/src/mscorlib/corefx/System/IO/PathInternal.Windows.StringBuffer.cs
src/coreclr/src/mscorlib/corefx/System/IO/PathInternal.Windows.cs
src/coreclr/src/mscorlib/corefx/System/Runtime/InteropServices/StringBuffer.cs
src/coreclr/src/mscorlib/src/System/Threading/Mutex.cs

index 2dd1907..c566fa0 100644 (file)
@@ -10,16 +10,11 @@ namespace System.IO
 {
     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";
 
@@ -47,12 +42,12 @@ namespace System.IO
             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;
         }
@@ -125,15 +120,15 @@ namespace System.IO
                     }
                 }
 
-                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;
                 }
 
@@ -169,7 +164,7 @@ namespace System.IO
             return
                 string.IsNullOrEmpty(path) ? DefaultTempPath :
                 PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? path :
-                path + DirectorySeparatorChar;
+                path + PathInternal.DirectorySeparatorChar;
         }
 
         public static string GetTempFileName()
@@ -197,58 +192,23 @@ namespace System.IO
                 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; } }
index b597efc..0c96769 100644 (file)
@@ -9,14 +9,8 @@ namespace System.IO
 {
     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, 
@@ -24,10 +18,18 @@ namespace System.IO
             (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)
@@ -64,9 +66,9 @@ namespace System.IO
                 // 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);
                 }
@@ -121,7 +123,7 @@ namespace System.IO
 
                 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;
index 3b1ba6b..77b2139 100644 (file)
@@ -13,10 +13,12 @@ namespace System.IO
     // 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.
@@ -90,11 +92,6 @@ namespace System.IO
             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
@@ -290,7 +287,7 @@ namespace System.IO
                     char ch = finalPath[finalPath.Length - 1];
                     if (!PathInternal.IsDirectoryOrVolumeSeparator(ch))
                     {
-                        finalPath.Append(DirectorySeparatorChar);
+                        finalPath.Append(PathInternal.DirectorySeparatorChar);
                     }
 
                     finalPath.Append(paths[i]);
@@ -314,7 +311,7 @@ namespace System.IO
             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)
@@ -340,11 +337,11 @@ namespace System.IO
             }
             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
             {
@@ -352,9 +349,9 @@ namespace System.IO
                 // 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);
             }
@@ -396,19 +393,19 @@ namespace System.IO
                 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);
index b6b7b61..7df435f 100644 (file)
@@ -11,7 +11,7 @@ namespace System.IO
     /// <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;
@@ -186,7 +186,7 @@ namespace System.IO
             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.
index 6c39f99..adc4da7 100644 (file)
@@ -10,11 +10,15 @@ namespace System.IO
     /// <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 = @"../";
 
@@ -34,8 +38,8 @@ namespace System.IO
         {
             // 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>
@@ -101,9 +105,9 @@ namespace System.IO
         {
             // 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)
index 712ba05..84953df 100644 (file)
@@ -86,7 +86,7 @@ namespace System.IO
             // 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]));
         }
     }
index d3e634e..560b9d1 100644 (file)
@@ -40,6 +40,13 @@ namespace System.IO
         // 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\";
@@ -58,22 +65,6 @@ namespace System.IO
         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>
@@ -193,10 +184,12 @@ namespace System.IO
             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;
+                    }
                 }
             }
 
@@ -206,13 +199,24 @@ namespace System.IO
         /// <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>
@@ -263,7 +267,7 @@ namespace System.IO
                     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
@@ -314,7 +318,7 @@ namespace System.IO
             // 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.
@@ -350,7 +354,7 @@ namespace System.IO
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static bool IsDirectorySeparator(char c)
         {
-            return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar;
+            return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
         }
 
         /// <summary>
@@ -401,7 +405,7 @@ namespace System.IO
                 {
                     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]))))
                     {
@@ -418,7 +422,7 @@ namespace System.IO
             if (IsDirectorySeparator(path[start]))
             {
                 start++;
-                builder.Append(Path.DirectorySeparatorChar);
+                builder.Append(DirectorySeparatorChar);
             }
 
             for (int i = start; i < path.Length; i++)
@@ -435,7 +439,7 @@ namespace System.IO
                     }
 
                     // Ensure it is the primary separator
-                    current = Path.DirectorySeparatorChar;
+                    current = DirectorySeparatorChar;
                 }
 
                 builder.Append(current);
@@ -450,7 +454,7 @@ namespace System.IO
         /// <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>
@@ -467,11 +471,11 @@ namespace System.IO
             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;
             }
index a7d9ed7..8779a63 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Buffers;
+using System.Runtime.CompilerServices;
 
 namespace System.Runtime.InteropServices
 {
@@ -39,11 +40,13 @@ 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));
@@ -233,7 +236,7 @@ namespace System.Runtime.InteropServices
         /// 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));
@@ -250,7 +253,7 @@ namespace System.Runtime.InteropServices
         /// 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));
index 506abb7..5ffeecd 100644 (file)
@@ -153,7 +153,7 @@ namespace System.Threading
 #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:
@@ -294,7 +294,7 @@ namespace System.Threading
                 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