Currently, the overloaded versions of Trim, TrimStart and TrimEnd that accept a trimChar or trimChars only works with ReadOnlySpan<char>. There is no reason why these versions cannot be extended to any ReadOnlySpan<T>. For example:
c#
public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, T trimElement)
{
int end = span.Length - 1;
for (; end >= 0; end--)
{
if (span[end] != trimElement)
break;
}
return span.Slice(0, end + 1);
}
This will allow, for example, quick processing of a byte array to trim off zero paddings before parsing.
@schungx can you update your code sample so that the return is outside the loop?
Edit: to be correct: remove the extra }?
if (span[end] != trimElement)
You can't use != with arbitrary Ts like this. You would instead, for example, need to constraint T with where T : IEquatable<T> and then use a comparison like:
C#
if (trimElement == null)
{
if (span[end] != null) break;
}
else
{
if (span[end] != null && !span[end].Equals(trimElement)) break;
}
or something like that.
To confirm, these are the APIs being proposed, correct?
C#
public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, T trimElement) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, System.ReadOnlySpan<T> trimElements) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, T trimElement) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, System.ReadOnlySpan<T> trimElements) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, T trimElement) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, System.ReadOnlySpan<T> trimElements) where T : IEquatable<T> { throw null; }
We can probably cut the APIs that don't specify a element (because default(T) doesn't seem reasonable) but it's still 6 APIs. We feel like we need a bit more justification to add these APIs.
@schungx what are the scenarios where you needed this API?
Also, the original ask (from title) is also for Memory and Span. Do we need those? If not, let's change the title.
Also, the original ask (from title) is also for
MemoryandSpan. Do we need those? If not, let's change the title.
We already have the T=char Trim APIs approved for ReadOnlyMemory. I would assume the same rationale would apply here for the generic ReadOnlyMemory. So, if we add the span-based ones, we should add the memory-based ones too.
@schungx what are the scenarios where you needed this API?
Well, I came about this when needing to process zero-padded byte data obtained via a Socket. So naturally something like TrimEnd for Span<byte> where I can specify the trimElement to be zero is immensely useful.
I can see this being useful in cases where you need to handle fixed-sized records with some default padding value.
Therefore, if 6 API's are too many, I would recommend just adding the two TrimEnd variations:
~
public static ReadOnlySpan
public static ReadOnlySpan
~
This is because TrimStart and Trim are really not very meaningful except for strings.
If we are going to add TrimEnd, we should add TrimStart too.
C#
public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, T trimElement) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, System.ReadOnlySpan<T> trimElements) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, T trimElement) where T : IEquatable<T> { throw null; }
public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, System.ReadOnlySpan<T> trimElements) where T : IEquatable<T> { throw null; }
ReadOnlyMemory<char> to be over T (except for the one that doesn't take values and assumes whistespace, which should remain to be over char).```C#
// Existing, effectively dead methods:
Trim(ReadOnlySpan
Trim(ReadOnlySpan
TrimEnd(ReadOnlySpan
TrimEnd(ReadOnlySpan
TrimStart(ReadOnlySpan
TrimStart(ReadOnlySpan
// Effective method set
//Trim(ReadOnlySpan
Trim(ReadOnlySpan
Trim(ReadOnlySpan
//TrimEnd(ReadOnlySpan
TrimEnd(ReadOnlySpan
TrimEnd(ReadOnlySpan
//TrimStart(ReadOnlySpan
TrimStart(ReadOnlySpan
TrimStart(ReadOnlySpan
Trim(Span
Trim(Span
Trim(Span
TrimEnd(Span
TrimEnd(Span
TrimEnd(Span
TrimStart(Span
TrimStart(Span
TrimStart(Span
Trim(ReadOnlyMemory
Trim(ReadOnlyMemory
Trim(ReadOnlyMemory
TrimEnd(ReadOnlyMemory
TrimEnd(ReadOnlyMemory
TrimEnd(ReadOnlyMemory
TrimStart(ReadOnlyMemory
TrimStart(ReadOnlyMemory
TrimStart(ReadOnlyMemory
Trim(Memory
Trim(Memory
Trim(Memory
TrimEnd(Memory
TrimEnd(Memory
TrimEnd(Memory
TrimStart(Memory
TrimStart(Memory
TrimStart(Memory
```
We should replace the already approved methods for
ReadOnlyMemory<char>to be overT(except for the one that doesn't take values and assumes whistespace, which should remain to be overchar).
i.e. https://github.com/dotnet/corefx/issues/30592, for reference.
or something like that.
I submitted the above PR, using the suggested comparison but moving it outside the loop to make it cheaper.
Not sure what the final set of required signatures is, it's a confusing thread above.
Is @terrajobst's list the final say?
I have implemented Trim, TrimStart and TrimEnd on RoS only.
Let me know if I need to add any of Span, Memory and RoM
[Edited]