// 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;
}
}
}