Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/IO/PathInternal.cs')
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/PathInternal.cs129
1 files changed, 124 insertions, 5 deletions
diff --git a/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs b/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs
index eb06c2608..1b08a2612 100644
--- a/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/PathInternal.cs
@@ -2,6 +2,9 @@
// 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.Diagnostics;
+using System.Text;
+
namespace System.IO
{
/// <summary>Contains internal path helpers that are shared between many projects.</summary>
@@ -19,10 +22,10 @@ namespace System.IO
internal static bool StartsWithDirectorySeparator(ReadOnlySpan<char> path) => path.Length > 0 && IsDirectorySeparator(path[0]);
internal static string EnsureTrailingSeparator(string path)
- => EndsInDirectorySeparator(path) ? path : path + DirectorySeparatorCharAsString;
+ => EndsInDirectorySeparator(path.AsSpan()) ? path : path + DirectorySeparatorCharAsString;
internal static string TrimEndingDirectorySeparator(string path) =>
- EndsInDirectorySeparator(path) && !IsRoot(path) ?
+ EndsInDirectorySeparator(path.AsSpan()) && !IsRoot(path.AsSpan()) ?
path.Substring(0, path.Length - 1) :
path;
@@ -63,7 +66,7 @@ namespace System.IO
/// <summary>
/// Gets the count of common characters from the left optionally ignoring case
/// </summary>
- unsafe internal static int EqualStartingCharacterCount(string first, string second, bool ignoreCase)
+ internal static unsafe int EqualStartingCharacterCount(string first, string second, bool ignoreCase)
{
if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0;
@@ -94,8 +97,8 @@ namespace System.IO
/// </summary>
internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType)
{
- int firstRootLength = GetRootLength(first);
- int secondRootLength = GetRootLength(second);
+ int firstRootLength = GetRootLength(first.AsSpan());
+ int secondRootLength = GetRootLength(second.AsSpan());
return firstRootLength == secondRootLength
&& string.Compare(
@@ -106,5 +109,121 @@ namespace System.IO
length: firstRootLength,
comparisonType: comparisonType) == 0;
}
+
+ /// <summary>
+ /// Try to remove relative segments from the given path (without combining with a root).
+ /// </summary>
+ /// <param name="rootLength">The length of the root of the given path</param>
+ internal static string RemoveRelativeSegments(string path, int rootLength)
+ {
+ Span<char> initialBuffer = stackalloc char[260 /* PathInternal.MaxShortPath */];
+ ValueStringBuilder sb = new ValueStringBuilder(initialBuffer);
+
+ if (RemoveRelativeSegments(path.AsSpan(), rootLength, ref sb))
+ {
+ path = sb.ToString();
+ }
+
+ sb.Dispose();
+ return path;
+ }
+
+ /// <summary>
+ /// Try to remove relative segments from the given path (without combining with a root).
+ /// </summary>
+ /// <param name="rootLength">The length of the root of the given path</param>
+ /// <returns>"true" if the path was modified</returns>
+ internal static bool RemoveRelativeSegments(ReadOnlySpan<char> path, int rootLength, ref ValueStringBuilder sb)
+ {
+ Debug.Assert(rootLength > 0);
+ bool flippedSeparator = false;
+
+ int skip = rootLength;
+ // We treat "\.." , "\." and "\\" as a relative segment. We want to collapse the first separator past the root presuming
+ // the root actually ends in a separator. Otherwise the first segment for RemoveRelativeSegments
+ // in cases like "\\?\C:\.\" and "\\?\C:\..\", the first segment after the root will be ".\" and "..\" which is not considered as a relative segment and hence not be removed.
+ if (PathInternal.IsDirectorySeparator(path[skip - 1]))
+ skip--;
+
+ // 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.
+ if (skip > 0)
+ {
+ sb.Append(path.Slice(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 >= skip; s--)
+ {
+ if (PathInternal.IsDirectorySeparator(sb[s]))
+ {
+ sb.Length = (i + 3 >= path.Length && s == skip) ? s + 1 : s; // to avoid removing the complete "\tmp\" segment in cases like \\?\C:\tmp\..\, C:\tmp\..
+ break;
+ }
+ }
+ if (s < skip)
+ {
+ sb.Length = skip;
+ }
+
+ 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 we haven't changed the source path, return the original
+ if (!flippedSeparator && sb.Length == path.Length)
+ {
+ return false;
+ }
+
+ // We may have eaten the trailing separator from the root when we started and not replaced it
+ if (skip != rootLength && sb.Length < rootLength)
+ {
+ sb.Append(path[rootLength - 1]);
+ }
+
+ return true;
+ }
}
}