// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. public static string NormalizeInternal(string inPath) { // Relative paths aren't a thing for IFileSystem, so assume all paths are absolute // and add a '/' to the beginning of the path if it doesn't already begin with one if (inPath.Length == 0 || !IsDirectorySeparator(inPath[0])) { inPath = DirectorySeparator + inPath; } ReadOnlySpan <char> path = inPath.AsSpan(); if (path.Length == 0) { return(DirectorySeparator.ToString()); } Span <char> initialBuffer = stackalloc char[0x200]; var sb = new ValueStringBuilder(initialBuffer); for (int i = 0; i < path.Length; i++) { char c = path[i]; if (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 (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 (IsCurrentDirectory(path, i)) { 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 (IsParentDirectory(path, i)) { // Unwind back to the last slash (and if there isn't one, clear out everything). for (int s = sb.Length - 1; s >= 0; s--) { if (IsDirectorySeparator(sb[s])) { sb.Length = s; break; } } i += 2; continue; } } sb.Append(c); } // If we haven't changed the source path, return the original if (sb.Length == inPath.Length) { return(inPath); } if (sb.Length == 0) { sb.Append(DirectorySeparator); } return(sb.ToString()); }
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. internal static bool NormalizeInternal(ReadOnlySpan <char> path, int rootLength, ref ValueStringBuilder sb) { if (rootLength > 0) { sb.Append(path.Slice(0, rootLength)); } bool isNormalized = true; var state = NormalizeState.Initial; for (int i = rootLength; i < path.Length; i++) { char c = path[i]; switch (state) { case NormalizeState.Initial when IsDirectorySeparator(c): state = NormalizeState.Delimiter; sb.Append(c); break; case NormalizeState.Initial when c == '.': isNormalized = false; state = NormalizeState.Dot; sb.Append(DirectorySeparator); sb.Append(c); break; case NormalizeState.Initial: isNormalized = false; state = NormalizeState.Normal; sb.Append(DirectorySeparator); sb.Append(c); break; case NormalizeState.Normal when IsDirectorySeparator(c): state = NormalizeState.Delimiter; sb.Append(c); break; case NormalizeState.Normal: sb.Append(c); break; case NormalizeState.Delimiter when IsDirectorySeparator(c): isNormalized = false; break; case NormalizeState.Delimiter when c == '.': state = NormalizeState.Dot; sb.Append(c); break; case NormalizeState.Delimiter: state = NormalizeState.Normal; sb.Append(c); break; case NormalizeState.Dot when IsDirectorySeparator(c): isNormalized = false; state = NormalizeState.Delimiter; sb.Length -= 1; break; case NormalizeState.Dot when c == '.': state = NormalizeState.DoubleDot; sb.Append(c); break; case NormalizeState.Dot: state = NormalizeState.Normal; sb.Append(c); break; case NormalizeState.DoubleDot when IsDirectorySeparator(c): isNormalized = false; state = NormalizeState.Delimiter; int s = sb.Length - 1; int separators = 0; for (; s > rootLength; s--) { if (IsDirectorySeparator(sb[s])) { separators++; if (separators == 2) { break; } } } sb.Length = s + 1; break; case NormalizeState.DoubleDot: state = NormalizeState.Normal; break; } } switch (state) { case NormalizeState.Dot: isNormalized = false; sb.Length -= 2; break; case NormalizeState.DoubleDot: isNormalized = false; int s = sb.Length - 1; int separators = 0; for (; s > rootLength; s--) { if (IsDirectorySeparator(sb[s])) { separators++; if (separators == 2) { break; } } } sb.Length = s; break; } if (sb.Length == rootLength) { sb.Append(DirectorySeparator); return(false); } return(isNormalized); }