diff options
author | Grant <grant@jesanna.com> | 2019-03-19 14:20:00 -0700 |
---|---|---|
committer | Stephen Toub <stoub@microsoft.com> | 2019-03-19 17:20:00 -0400 |
commit | 860c72845fa6ac501d11f6b71d0b79f4294745d6 (patch) | |
tree | 764249c247e6e1203b9232b13bfb4ac89051b262 | |
parent | 5aacb1dfa019eb4865800d05a408665be322ea77 (diff) | |
download | coreclr-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
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) |