summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrant <grant@jesanna.com>2019-03-19 14:20:00 -0700
committerStephen Toub <stoub@microsoft.com>2019-03-19 17:20:00 -0400
commit860c72845fa6ac501d11f6b71d0b79f4294745d6 (patch)
tree764249c247e6e1203b9232b13bfb4ac89051b262
parent5aacb1dfa019eb4865800d05a408665be322ea77 (diff)
downloadcoreclr-860c72845fa6ac501d11f6b71d0b79f4294745d6.tar.gz
coreclr-860c72845fa6ac501d11f6b71d0b79f4294745d6.tar.bz2
coreclr-860c72845fa6ac501d11f6b71d0b79f4294745d6.zip
Trim/TrimStart/TrimEnd methods for Memory and Span (#22798)
* Span.Trim * Add another helper * Remove redundant comments * Optimize Trim(ReadOnlySpan<char>) * Suggested overload for byte * CR fixes * CR fix * Add Span overloads * Add Memory overloads * CR * Rename method * Optimization * Tweaks * Refactor * Opt * Use 2 helpers * CR fixes * CR fixes * Nits * CR fixes * remove in params * Bug * Bug * Move Trim methods to their own dedicated file * Add char overloads * Use Contains * CR fixes * Optmize ClampNull * Nit * Revert optimization * Make existing implementations consistent with new * Revert existing methods * Move helpers * Span, Memory and RoM should use consistent pattern * POC for changing RoS * Revert POC * Use consistent idiom * More inlining of explaining vars * Use expression body for existing code * CR fixes * Nit * Grammar * CR fixes * Fix perf bug in existing code * Perf fix * Force CI build * Force CI
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems1
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs866
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.cs172
3 files changed, 867 insertions, 172 deletions
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
index b1c9da0bfa..4b5bd02062 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -360,6 +360,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryDebugView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Fast.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Trim.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MethodAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MidpointRounding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MissingFieldException.cs" />
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs
new file mode 100644
index 0000000000..be98bb1fba
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs
@@ -0,0 +1,866 @@
+// 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.
+
+using System.Diagnostics;
+
+namespace System
+{
+ public static partial class MemoryExtensions
+ {
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> Trim<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> TrimStart<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(ClampStart(memory.Span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> TrimEnd<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> Trim<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> TrimStart<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(ClampStart(memory.Span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> TrimEnd<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> Trim<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> TrimStart<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(ClampStart(span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> TrimEnd<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(0, ClampEnd(span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(ClampStart(span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(0, ClampEnd(span, 0, trimElement));
+
+ /// <summary>
+ /// Delimits all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ private static int ClampStart<T>(ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = 0;
+
+ if (trimElement != null)
+ {
+ for (; start < span.Length; start++)
+ {
+ if (!trimElement.Equals(span[start]))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ private static int ClampEnd<T>(ReadOnlySpan<T> span, int start, T trimElement)
+ where T : IEquatable<T>
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+
+ if (trimElement != null)
+ {
+ for (; end >= start; end--)
+ {
+ if (!trimElement.Equals(span[end]))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (; end >= start; end--)
+ {
+ if (span[end] != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return end - start + 1;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> Trim<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return memory.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> TrimStart<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(ClampStart(memory.Span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> TrimEnd<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> Trim<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return memory.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> TrimStart<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(ClampStart(memory.Span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> TrimEnd<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(memory, trimElements[0]);
+ }
+
+ return memory;
+
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> Trim<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return span.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> TrimStart<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(ClampStart(span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> TrimEnd<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(0, ClampEnd(span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return span.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(ClampStart(span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(0, ClampEnd(span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Delimits all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ private static int ClampStart<T>(ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!trimElements.Contains(span[start]))
+ {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ private static int ClampEnd<T>(ReadOnlySpan<T> span, int start, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+ for (; end >= start; end--)
+ {
+ if (!trimElements.Contains(span[end]))
+ {
+ break;
+ }
+ }
+
+ return end - start + 1;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> Trim(this Memory<char> memory)
+ {
+ ReadOnlySpan<char> span = memory.Span;
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> TrimStart(this Memory<char> memory)
+ => memory.Slice(ClampStart(memory.Span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> TrimEnd(this Memory<char> memory)
+ => memory.Slice(0, ClampEnd(memory.Span, 0));
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> Trim(this ReadOnlyMemory<char> memory)
+ {
+ ReadOnlySpan<char> span = memory.Span;
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> TrimStart(this ReadOnlyMemory<char> memory)
+ => memory.Slice(ClampStart(memory.Span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> TrimEnd(this ReadOnlyMemory<char> memory)
+ => memory.Slice(0, ClampEnd(memory.Span, 0));
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ int end = span.Length - 1;
+ for (; end > start; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start, end - start + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span)
+ {
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified character from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != trimChar)
+ {
+ break;
+ }
+ }
+
+ int end = span.Length - 1;
+ for (; end > start; end--)
+ {
+ if (span[end] != trimChar)
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start, end - start + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified character from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != trimChar)
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified character from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (span[end] != trimChar)
+ {
+ break;
+ }
+ }
+
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ => span.TrimStart(trimChars).TrimEnd(trimChars);
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ if (trimChars.IsEmpty)
+ {
+ return span.TrimStart();
+ }
+
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ for (int i = 0; i < trimChars.Length; i++)
+ {
+ if (span[start] == trimChars[i])
+ {
+ goto Next;
+ }
+ }
+
+ break;
+ Next:
+ ;
+ }
+
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ if (trimChars.IsEmpty)
+ {
+ return span.TrimEnd();
+ }
+
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ for (int i = 0; i < trimChars.Length; i++)
+ {
+ if (span[end] == trimChars[i])
+ {
+ goto Next;
+ }
+ }
+
+ break;
+ Next:
+ ;
+ }
+
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> Trim(this Span<char> span)
+ {
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> TrimStart(this Span<char> span)
+ => span.Slice(ClampStart(span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> TrimEnd(this Span<char> span)
+ => span.Slice(0, ClampEnd(span, 0));
+
+ /// <summary>
+ /// Delimits all leading occurrences of whitespace charecters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ private static int ClampStart(ReadOnlySpan<char> span)
+ {
+ int start = 0;
+
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of whitespace charecters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ private static int ClampEnd(ReadOnlySpan<char> span, int start)
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+
+ for (; end >= start; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return end - start + 1;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
index 151ee323be..2b7861f3f5 100644
--- a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
@@ -23,178 +23,6 @@ namespace System
public static partial class MemoryExtensions
{
/// <summary>
- /// Removes all leading and trailing white-space characters from the span.
- /// </summary>
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (!char.IsWhiteSpace(span[start]))
- break;
- }
- int end = span.Length - 1;
- for (; end >= start; end--)
- {
- if (!char.IsWhiteSpace(span[end]))
- break;
- }
- return span.Slice(start, end - start + 1);
- }
-
- /// <summary>
- /// Removes all leading white-space characters from the span.
- /// </summary>
- public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (!char.IsWhiteSpace(span[start]))
- break;
- }
- return span.Slice(start);
- }
-
- /// <summary>
- /// Removes all trailing white-space characters from the span.
- /// </summary>
- public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span)
- {
- int end = span.Length - 1;
- for (; end >= 0; end--)
- {
- if (!char.IsWhiteSpace(span[end]))
- break;
- }
- return span.Slice(0, end + 1);
- }
-
- /// <summary>
- /// Removes all leading and trailing occurrences of a specified character.
- /// </summary>
- /// <param name="span">The source span from which the character is removed.</param>
- /// <param name="trimChar">The specified character to look for and remove.</param>
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (span[start] != trimChar)
- break;
- }
- int end = span.Length - 1;
- for (; end >= start; end--)
- {
- if (span[end] != trimChar)
- break;
- }
- return span.Slice(start, end - start + 1);
- }
-
- /// <summary>
- /// Removes all leading occurrences of a specified character.
- /// </summary>
- /// <param name="span">The source span from which the character is removed.</param>
- /// <param name="trimChar">The specified character to look for and remove.</param>
- public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (span[start] != trimChar)
- break;
- }
- return span.Slice(start);
- }
-
- /// <summary>
- /// Removes all trailing occurrences of a specified character.
- /// </summary>
- /// <param name="span">The source span from which the character is removed.</param>
- /// <param name="trimChar">The specified character to look for and remove.</param>
- public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, char trimChar)
- {
- int end = span.Length - 1;
- for (; end >= 0; end--)
- {
- if (span[end] != trimChar)
- break;
- }
- return span.Slice(0, end + 1);
- }
-
- /// <summary>
- /// Removes all leading and trailing occurrences of a set of characters specified
- /// in a readonly span from the span.
- /// </summary>
- /// <param name="span">The source span from which the characters are removed.</param>
- /// <param name="trimChars">The span which contains the set of characters to remove.</param>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- return span.TrimStart(trimChars).TrimEnd(trimChars);
- }
-
- /// <summary>
- /// Removes all leading occurrences of a set of characters specified
- /// in a readonly span from the span.
- /// </summary>
- /// <param name="span">The source span from which the characters are removed.</param>
- /// <param name="trimChars">The span which contains the set of characters to remove.</param>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- if (trimChars.IsEmpty)
- {
- return span.TrimStart();
- }
-
- int start = 0;
- for (; start < span.Length; start++)
- {
- for (int i = 0; i < trimChars.Length; i++)
- {
- if (span[start] == trimChars[i])
- goto Next;
- }
- break;
- Next:
- ;
- }
- return span.Slice(start);
- }
-
- /// <summary>
- /// Removes all trailing occurrences of a set of characters specified
- /// in a readonly span from the span.
- /// </summary>
- /// <param name="span">The source span from which the characters are removed.</param>
- /// <param name="trimChars">The span which contains the set of characters to remove.</param>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- if (trimChars.IsEmpty)
- {
- return span.TrimEnd();
- }
-
- int end = span.Length - 1;
- for (; end >= 0; end--)
- {
- for (int i = 0; i < trimChars.Length; i++)
- {
- if (span[end] == trimChars[i])
- goto Next;
- }
- break;
- Next:
- ;
- }
- return span.Slice(0, end + 1);
- }
-
- /// <summary>
/// Indicates whether the specified span contains only white-space characters.
/// </summary>
public static bool IsWhiteSpace(this ReadOnlySpan<char> span)