/// <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;
if (PathInternal.IsDirectorySeparator(path[skip - 1]))
skip--;
- Span<char> initialBuffer = stackalloc char[260 /* PathInternal.MaxShortPath */];
- ValueStringBuilder sb = new ValueStringBuilder(initialBuffer);
-
// 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.AsSpan(0, skip));
+ sb.Append(path.Slice(0, skip));
}
for (int i = skip; i < path.Length; i++)
i += 2;
continue;
- }
+ }
}
// Normalize the directory separator if needed
// If we haven't changed the source path, return the original
if (!flippedSeparator && sb.Length == path.Length)
{
- sb.Dispose();
- return path;
+ 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 sb.Length < rootLength ? path.Substring(0, rootLength) : sb.ToString();
+ return true;
}
}
}