// 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 { /// /// Removes all leading and trailing occurrences of a specified element from the memory. /// /// The source memory from which the element is removed. /// The specified element to look for and remove. public static Memory Trim(this Memory memory, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { ReadOnlySpan span = memory.Span; int start = ClampStart(span, trimElement); int length = ClampEnd(span, start, trimElement); return memory.Slice(start, length); } /// /// Removes all leading occurrences of a specified element from the memory. /// /// The source memory from which the element is removed. /// The specified element to look for and remove. public static Memory TrimStart(this Memory memory, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => memory.Slice(ClampStart(memory.Span, trimElement)); /// /// Removes all trailing occurrences of a specified element from the memory. /// /// The source memory from which the element is removed. /// The specified element to look for and remove. public static Memory TrimEnd(this Memory memory, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement)); /// /// Removes all leading and trailing occurrences of a specified element from the memory. /// /// The source memory from which the element is removed. /// The specified element to look for and remove. public static ReadOnlyMemory Trim(this ReadOnlyMemory memory, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { ReadOnlySpan span = memory.Span; int start = ClampStart(span, trimElement); int length = ClampEnd(span, start, trimElement); return memory.Slice(start, length); } /// /// Removes all leading occurrences of a specified element from the memory. /// /// The source memory from which the element is removed. /// The specified element to look for and remove. public static ReadOnlyMemory TrimStart(this ReadOnlyMemory memory, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => memory.Slice(ClampStart(memory.Span, trimElement)); /// /// Removes all trailing occurrences of a specified element from the memory. /// /// The source memory from which the element is removed. /// The specified element to look for and remove. public static ReadOnlyMemory TrimEnd(this ReadOnlyMemory memory, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement)); /// /// Removes all leading and trailing occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The specified element to look for and remove. public static Span Trim(this Span span, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { int start = ClampStart(span, trimElement); int length = ClampEnd(span, start, trimElement); return span.Slice(start, length); } /// /// Removes all leading occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The specified element to look for and remove. public static Span TrimStart(this Span span, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => span.Slice(ClampStart(span, trimElement)); /// /// Removes all trailing occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The specified element to look for and remove. public static Span TrimEnd(this Span span, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => span.Slice(0, ClampEnd(span, 0, trimElement)); /// /// Removes all leading and trailing occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The specified element to look for and remove. public static ReadOnlySpan Trim(this ReadOnlySpan span, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { int start = ClampStart(span, trimElement); int length = ClampEnd(span, start, trimElement); return span.Slice(start, length); } /// /// Removes all leading occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The specified element to look for and remove. public static ReadOnlySpan TrimStart(this ReadOnlySpan span, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => span.Slice(ClampStart(span, trimElement)); /// /// Removes all trailing occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The specified element to look for and remove. public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore => span.Slice(0, ClampEnd(span, 0, trimElement)); /// /// Delimits all leading occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The specified element to look for and remove. private static int ClampStart(ReadOnlySpan span, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { 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; } /// /// Delimits all trailing occurrences of a specified element from the span. /// /// The source span from which the element is removed. /// The start index from which to being searching. /// The specified element to look for and remove. private static int ClampEnd(ReadOnlySpan span, int start, T trimElement) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { // 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; } /// /// Removes all leading and trailing occurrences of a set of elements specified /// in a readonly span from the memory. /// /// The source memory from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the memory is returned unaltered. public static Memory Trim(this Memory memory, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { ReadOnlySpan span = memory.Span; int start = ClampStart(span, trimElements); int length = ClampEnd(span, start, trimElements); return memory.Slice(start, length); } if (trimElements.Length == 1) { return Trim(memory, trimElements[0]); } return memory; } /// /// Removes all leading occurrences of a set of elements specified /// in a readonly span from the memory. /// /// The source memory from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the memory is returned unaltered. public static Memory TrimStart(this Memory memory, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return memory.Slice(ClampStart(memory.Span, trimElements)); } if (trimElements.Length == 1) { return TrimStart(memory, trimElements[0]); } return memory; } /// /// Removes all trailing occurrences of a set of elements specified /// in a readonly span from the memory. /// /// The source memory from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the memory is returned unaltered. public static Memory TrimEnd(this Memory memory, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements)); } if (trimElements.Length == 1) { return TrimEnd(memory, trimElements[0]); } return memory; } /// /// Removes all leading and trailing occurrences of a set of elements specified /// in a readonly span from the memory. /// /// The source memory from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the memory is returned unaltered. public static ReadOnlyMemory Trim(this ReadOnlyMemory memory, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { ReadOnlySpan span = memory.Span; int start = ClampStart(span, trimElements); int length = ClampEnd(span, start, trimElements); return memory.Slice(start, length); } if (trimElements.Length == 1) { return Trim(memory, trimElements[0]); } return memory; } /// /// Removes all leading occurrences of a set of elements specified /// in a readonly span from the memory. /// /// The source memory from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the memory is returned unaltered. public static ReadOnlyMemory TrimStart(this ReadOnlyMemory memory, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return memory.Slice(ClampStart(memory.Span, trimElements)); } if (trimElements.Length == 1) { return TrimStart(memory, trimElements[0]); } return memory; } /// /// Removes all trailing occurrences of a set of elements specified /// in a readonly span from the memory. /// /// The source memory from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the memory is returned unaltered. public static ReadOnlyMemory TrimEnd(this ReadOnlyMemory memory, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements)); } if (trimElements.Length == 1) { return TrimEnd(memory, trimElements[0]); } return memory; } /// /// Removes all leading and trailing occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the span is returned unaltered. public static Span Trim(this Span span, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { int start = ClampStart(span, trimElements); int length = ClampEnd(span, start, trimElements); return span.Slice(start, length); } if (trimElements.Length == 1) { return Trim(span, trimElements[0]); } return span; } /// /// Removes all leading occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the span is returned unaltered. public static Span TrimStart(this Span span, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return span.Slice(ClampStart(span, trimElements)); } if (trimElements.Length == 1) { return TrimStart(span, trimElements[0]); } return span; } /// /// Removes all trailing occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the span is returned unaltered. public static Span TrimEnd(this Span span, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return span.Slice(0, ClampEnd(span, 0, trimElements)); } if (trimElements.Length == 1) { return TrimEnd(span, trimElements[0]); } return span; } /// /// Removes all leading and trailing occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the span is returned unaltered. public static ReadOnlySpan Trim(this ReadOnlySpan span, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { int start = ClampStart(span, trimElements); int length = ClampEnd(span, start, trimElements); return span.Slice(start, length); } if (trimElements.Length == 1) { return Trim(span, trimElements[0]); } return span; } /// /// Removes all leading occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the span is returned unaltered. public static ReadOnlySpan TrimStart(this ReadOnlySpan span, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return span.Slice(ClampStart(span, trimElements)); } if (trimElements.Length == 1) { return TrimStart(span, trimElements[0]); } return span; } /// /// Removes all trailing occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The span which contains the set of elements to remove. /// If is empty, the span is returned unaltered. public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { if (trimElements.Length > 1) { return span.Slice(0, ClampEnd(span, 0, trimElements)); } if (trimElements.Length == 1) { return TrimEnd(span, trimElements[0]); } return span; } /// /// Delimits all leading occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The span which contains the set of elements to remove. private static int ClampStart(ReadOnlySpan span, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { int start = 0; for (; start < span.Length; start++) { if (!trimElements.Contains(span[start])) { break; } } return start; } /// /// Delimits all trailing occurrences of a set of elements specified /// in a readonly span from the span. /// /// The source span from which the elements are removed. /// The start index from which to being searching. /// The span which contains the set of elements to remove. private static int ClampEnd(ReadOnlySpan span, int start, ReadOnlySpan trimElements) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { // 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; } /// /// Removes all leading and trailing white-space characters from the memory. /// /// The source memory from which the characters are removed. public static Memory Trim(this Memory memory) { ReadOnlySpan span = memory.Span; int start = ClampStart(span); int length = ClampEnd(span, start); return memory.Slice(start, length); } /// /// Removes all leading white-space characters from the memory. /// /// The source memory from which the characters are removed. public static Memory TrimStart(this Memory memory) => memory.Slice(ClampStart(memory.Span)); /// /// Removes all trailing white-space characters from the memory. /// /// The source memory from which the characters are removed. public static Memory TrimEnd(this Memory memory) => memory.Slice(0, ClampEnd(memory.Span, 0)); /// /// Removes all leading and trailing white-space characters from the memory. /// /// The source memory from which the characters are removed. public static ReadOnlyMemory Trim(this ReadOnlyMemory memory) { ReadOnlySpan span = memory.Span; int start = ClampStart(span); int length = ClampEnd(span, start); return memory.Slice(start, length); } /// /// Removes all leading white-space characters from the memory. /// /// The source memory from which the characters are removed. public static ReadOnlyMemory TrimStart(this ReadOnlyMemory memory) => memory.Slice(ClampStart(memory.Span)); /// /// Removes all trailing white-space characters from the memory. /// /// The source memory from which the characters are removed. public static ReadOnlyMemory TrimEnd(this ReadOnlyMemory memory) => memory.Slice(0, ClampEnd(memory.Span, 0)); /// /// Removes all leading and trailing white-space characters from the span. /// /// The source span from which the characters are removed. public static ReadOnlySpan Trim(this ReadOnlySpan 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); } /// /// Removes all leading white-space characters from the span. /// /// The source span from which the characters are removed. public static ReadOnlySpan TrimStart(this ReadOnlySpan span) { int start = 0; for (; start < span.Length; start++) { if (!char.IsWhiteSpace(span[start])) { break; } } return span.Slice(start); } /// /// Removes all trailing white-space characters from the span. /// /// The source span from which the characters are removed. public static ReadOnlySpan TrimEnd(this ReadOnlySpan span) { int end = span.Length - 1; for (; end >= 0; end--) { if (!char.IsWhiteSpace(span[end])) { break; } } return span.Slice(0, end + 1); } /// /// Removes all leading and trailing occurrences of a specified character from the span. /// /// The source span from which the character is removed. /// The specified character to look for and remove. public static ReadOnlySpan Trim(this ReadOnlySpan 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); } /// /// Removes all leading occurrences of a specified character from the span. /// /// The source span from which the character is removed. /// The specified character to look for and remove. public static ReadOnlySpan TrimStart(this ReadOnlySpan span, char trimChar) { int start = 0; for (; start < span.Length; start++) { if (span[start] != trimChar) { break; } } return span.Slice(start); } /// /// Removes all trailing occurrences of a specified character from the span. /// /// The source span from which the character is removed. /// The specified character to look for and remove. public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, char trimChar) { int end = span.Length - 1; for (; end >= 0; end--) { if (span[end] != trimChar) { break; } } return span.Slice(0, end + 1); } /// /// Removes all leading and trailing occurrences of a set of characters specified /// in a readonly span from the span. /// /// The source span from which the characters are removed. /// The span which contains the set of characters to remove. /// If is empty, white-space characters are removed instead. public static ReadOnlySpan Trim(this ReadOnlySpan span, ReadOnlySpan trimChars) => span.TrimStart(trimChars).TrimEnd(trimChars); /// /// Removes all leading occurrences of a set of characters specified /// in a readonly span from the span. /// /// The source span from which the characters are removed. /// The span which contains the set of characters to remove. /// If is empty, white-space characters are removed instead. public static ReadOnlySpan TrimStart(this ReadOnlySpan span, ReadOnlySpan 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); } /// /// Removes all trailing occurrences of a set of characters specified /// in a readonly span from the span. /// /// The source span from which the characters are removed. /// The span which contains the set of characters to remove. /// If is empty, white-space characters are removed instead. public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, ReadOnlySpan 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); } /// /// Removes all leading and trailing white-space characters from the span. /// /// The source span from which the characters are removed. public static Span Trim(this Span span) { int start = ClampStart(span); int length = ClampEnd(span, start); return span.Slice(start, length); } /// /// Removes all leading white-space characters from the span. /// /// The source span from which the characters are removed. public static Span TrimStart(this Span span) => span.Slice(ClampStart(span)); /// /// Removes all trailing white-space characters from the span. /// /// The source span from which the characters are removed. public static Span TrimEnd(this Span span) => span.Slice(0, ClampEnd(span, 0)); /// /// Delimits all leading occurrences of whitespace charecters from the span. /// /// The source span from which the characters are removed. private static int ClampStart(ReadOnlySpan span) { int start = 0; for (; start < span.Length; start++) { if (!char.IsWhiteSpace(span[start])) { break; } } return start; } /// /// Delimits all trailing occurrences of whitespace charecters from the span. /// /// The source span from which the characters are removed. /// The start index from which to being searching. private static int ClampEnd(ReadOnlySpan 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; } } }