diff options
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/IO/Path.cs')
-rw-r--r-- | src/System.Private.CoreLib/shared/System/IO/Path.cs | 187 |
1 files changed, 62 insertions, 125 deletions
diff --git a/src/System.Private.CoreLib/shared/System/IO/Path.cs b/src/System.Private.CoreLib/shared/System/IO/Path.cs index 41ae1cd0b..7b4565a13 100644 --- a/src/System.Private.CoreLib/shared/System/IO/Path.cs +++ b/src/System.Private.CoreLib/shared/System/IO/Path.cs @@ -6,7 +6,14 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; +#if MS_IO_REDIST +using System; +using System.IO; + +namespace Microsoft.IO +#else namespace System.IO +#endif { // Provides methods for processing file system strings in a cross-platform manner. // Most of the methods don't do a complete parsing (such as examining a UNC hostname), @@ -79,10 +86,10 @@ namespace System.IO /// </remarks> public static string GetDirectoryName(string path) { - if (path == null || PathInternal.IsEffectivelyEmpty(path)) + if (path == null || PathInternal.IsEffectivelyEmpty(path.AsSpan())) return null; - int end = GetDirectoryNameOffset(path); + int end = GetDirectoryNameOffset(path.AsSpan()); return end >= 0 ? PathInternal.NormalizeDirectorySeparators(path.Substring(0, end)) : null; } @@ -130,7 +137,7 @@ namespace System.IO if (path == null) return null; - return new string(GetExtension(path.AsSpan())); + return GetExtension(path.AsSpan()).ToString(); } /// <summary> @@ -173,7 +180,7 @@ namespace System.IO if (path.Length == result.Length) return path; - return new string(result); + return result.ToString(); } /// <summary> @@ -204,7 +211,7 @@ namespace System.IO if (path.Length == result.Length) return path; - return new string(result); + return result.ToString(); } /// <summary> @@ -388,9 +395,9 @@ namespace System.IO public static string Join(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2) { if (path1.Length == 0) - return new string(path2); + return path2.ToString(); if (path2.Length == 0) - return new string(path1); + return path1.ToString(); return JoinInternal(path1, path2); } @@ -409,6 +416,16 @@ namespace System.IO return JoinInternal(path1, path2, path3); } + public static string Join(string path1, string path2) + { + return Join(path1.AsSpan(), path2.AsSpan()); + } + + public static string Join(string path1, string path2, string path3) + { + return Join(path1.AsSpan(), path2.AsSpan(), path3.AsSpan()); + } + public static bool TryJoin(ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, Span<char> destination, out int charsWritten) { charsWritten = 0; @@ -477,22 +494,6 @@ namespace System.IO return true; } - private static string CombineInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second) - { - if (first.Length == 0) - return second.Length == 0 - ? string.Empty - : new string(second); - - if (second.Length == 0) - return new string(first); - - if (IsPathRooted(second)) - return new string(second); - - return JoinInternal(first, second); - } - private static string CombineInternal(string first, string second) { if (string.IsNullOrEmpty(first)) @@ -504,7 +505,7 @@ namespace System.IO if (IsPathRooted(second.AsSpan())) return second; - return JoinInternal(first, second); + return JoinInternal(first.AsSpan(), second.AsSpan()); } private static string CombineInternal(string first, string second, string third) @@ -521,7 +522,7 @@ namespace System.IO if (IsPathRooted(second.AsSpan())) return CombineInternal(second, third); - return JoinInternal(first, second, third); + return JoinInternal(first.AsSpan(), second.AsSpan(), third.AsSpan()); } private static string CombineInternal(string first, string second, string third, string fourth) @@ -542,10 +543,10 @@ namespace System.IO if (IsPathRooted(second.AsSpan())) return CombineInternal(second, third, fourth); - return JoinInternal(first, second, third, fourth); + return JoinInternal(first.AsSpan(), second.AsSpan(), third.AsSpan(), fourth.AsSpan()); } - private unsafe static string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second) + private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second) { Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths"); @@ -554,7 +555,11 @@ namespace System.IO fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second)) { +#if MS_IO_REDIST + return StringExtensions.Create( +#else return string.Create( +#endif first.Length + second.Length + (hasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator), (destination, state) => @@ -567,7 +572,7 @@ namespace System.IO } } - private unsafe static string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third) + private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third) { Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0, "should have dealt with empty paths"); @@ -578,7 +583,11 @@ namespace System.IO fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third)) { +#if MS_IO_REDIST + return StringExtensions.Create( +#else return string.Create( +#endif first.Length + second.Length + third.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, Third: (IntPtr)t, ThirdLength: third.Length, FirstHasSeparator: firstHasSeparator, ThirdHasSeparator: thirdHasSeparator), @@ -595,7 +604,7 @@ namespace System.IO } } - private unsafe static string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third, ReadOnlySpan<char> fourth) + private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second, ReadOnlySpan<char> third, ReadOnlySpan<char> fourth) { Debug.Assert(first.Length > 0 && second.Length > 0 && third.Length > 0 && fourth.Length > 0, "should have dealt with empty paths"); @@ -608,7 +617,12 @@ namespace System.IO fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second), t = &MemoryMarshal.GetReference(third), u = &MemoryMarshal.GetReference(fourth)) { + +#if MS_IO_REDIST + return StringExtensions.Create( +#else return string.Create( +#endif first.Length + second.Length + third.Length + fourth.Length + (firstHasSeparator ? 0 : 1) + (thirdHasSeparator ? 0 : 1) + (fourthHasSeparator ? 0 : 1), (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, Third: (IntPtr)t, ThirdLength: third.Length, Fourth: (IntPtr)u, FourthLength:fourth.Length, @@ -688,93 +702,6 @@ namespace System.IO } /// <summary> - /// Try to remove relative segments from the given path (without combining with a root). - /// </summary> - /// <param name="skip">Skip the specified number of characters before evaluating.</param> - private static string RemoveRelativeSegments(string path, int skip = 0) - { - bool flippedSeparator = false; - - // Remove "//", "/./", and "/../" from the path by copying each character to the output, - // except the ones we're removing, such that the builder contains the normalized path - // at the end. - StringBuilder sb = StringBuilderCache.Acquire(path.Length); - if (skip > 0) - { - sb.Append(path, 0, skip); - } - - for (int i = skip; i < path.Length; i++) - { - char c = path[i]; - - if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length) - { - // Skip this character if it's a directory separator and if the next character is, too, - // e.g. "parent//child" => "parent/child" - if (PathInternal.IsDirectorySeparator(path[i + 1])) - { - continue; - } - - // Skip this character and the next if it's referring to the current directory, - // e.g. "parent/./child" => "parent/child" - if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) && - path[i + 1] == '.') - { - i++; - continue; - } - - // Skip this character and the next two if it's referring to the parent directory, - // e.g. "parent/child/../grandchild" => "parent/grandchild" - if (i + 2 < path.Length && - (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) && - path[i + 1] == '.' && path[i + 2] == '.') - { - // Unwind back to the last slash (and if there isn't one, clear out everything). - int s; - for (s = sb.Length - 1; s >= 0; s--) - { - if (PathInternal.IsDirectorySeparator(sb[s])) - { - sb.Length = s; - break; - } - } - if (s < 0) - { - sb.Length = 0; - } - - i += 2; - continue; - } - } - - // Normalize the directory separator if needed - if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar) - { - c = PathInternal.DirectorySeparatorChar; - flippedSeparator = true; - } - - sb.Append(c); - } - - if (flippedSeparator || sb.Length != path.Length) - { - return StringBuilderCache.GetStringAndRelease(sb); - } - else - { - // We haven't changed the source path, return the original - StringBuilderCache.Release(sb); - return path; - } - } - - /// <summary> /// Create a relative path from one path to another. Paths will be resolved before calculating the difference. /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix). /// </summary> @@ -790,7 +717,7 @@ namespace System.IO private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) { if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo)); - if (PathInternal.IsEffectivelyEmpty(path)) throw new ArgumentNullException(nameof(path)); + if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) throw new ArgumentNullException(nameof(path)); Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); relativeTo = GetFullPath(relativeTo); @@ -808,10 +735,10 @@ namespace System.IO // Trailing separators aren't significant for comparison int relativeToLength = relativeTo.Length; - if (PathInternal.EndsInDirectorySeparator(relativeTo)) + if (PathInternal.EndsInDirectorySeparator(relativeTo.AsSpan())) relativeToLength--; - bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path); + bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path.AsSpan()); int pathLength = path.Length; if (pathEndsInSeparator) pathLength--; @@ -834,13 +761,14 @@ namespace System.IO // Add parent segments for segments past the common on the "from" path if (commonLength < relativeToLength) { - sb.Append(PathInternal.ParentDirectoryPrefix); + sb.Append(".."); - for (int i = commonLength; i < relativeToLength; i++) + for (int i = commonLength + 1; i < relativeToLength; i++) { if (PathInternal.IsDirectorySeparator(relativeTo[i])) { - sb.Append(PathInternal.ParentDirectoryPrefix); + sb.Append(DirectorySeparatorChar); + sb.Append(".."); } } } @@ -852,11 +780,20 @@ namespace System.IO } // Now add the rest of the "to" path, adding back the trailing separator - int count = pathLength - commonLength; + int differenceLength = pathLength - commonLength; if (pathEndsInSeparator) - count++; + differenceLength++; + + if (differenceLength > 0) + { + if (sb.Length > 0) + { + sb.Append(DirectorySeparatorChar); + } + + sb.Append(path, commonLength, differenceLength); + } - sb.Append(path, commonLength, count); return StringBuilderCache.GetStringAndRelease(sb); } |