Skip to content

Instantly share code, notes, and snippets.

@neon-sunset
Last active June 20, 2023 18:14
Show Gist options
  • Save neon-sunset/df6fb9fe6bb71f11c2b47fbeae55e6da to your computer and use it in GitHub Desktop.
Save neon-sunset/df6fb9fe6bb71f11c2b47fbeae55e6da to your computer and use it in GitHub Desktop.
SplitFirst and SplitLast convenience methods to handle the most common pair slicing pattern. Use .AsSpan() or .AsMemory() for alloction-free version.
// Proposal: https://github.com/dotnet/runtime/issues/75317
// This code is licensed under MIT license
// (c) 2022 neon-sunset
using System.Runtime.CompilerServices;
namespace System;
public static class SplitExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (string Segment, string Remainder) SplitFirst(this string source, char separator)
{
var (segment, remainder) = source
.AsSpan()
.SplitFirst(separator);
return (segment.ToString(), remainder.ToString());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SplitPair<T> SplitFirst<T>(this Span<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.IndexOf(separator);
return separatorIndex > -1
? new(source[..separatorIndex], source[(separatorIndex + 1)..])
: new(source, Span<T>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySplitPair<T> SplitFirst<T>(this ReadOnlySpan<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.IndexOf(separator);
return separatorIndex > -1
? new(source[..separatorIndex], source[(separatorIndex + 1)..])
: new(source, ReadOnlySpan<T>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (Memory<T> Segment, Memory<T> Remainder) SplitFirst<T>(this Memory<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.Span.IndexOf(separator);
return separatorIndex > -1
? (source[..separatorIndex], source[(separatorIndex + 1)..])
: (source, Memory<T>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (ReadOnlyMemory<T> Segment, ReadOnlyMemory<T> Remainder) SplitFirst<T>(this ReadOnlyMemory<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.Span.IndexOf(separator);
return separatorIndex > -1
? (source[..separatorIndex], source[(separatorIndex + 1)..])
: (source, ReadOnlyMemory<T>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (string Segment, string Remainder) SplitLast(this string source, char separator)
{
var (segment, remainder) = source
.AsSpan()
.SplitLast(separator);
return (segment.ToString(), remainder.ToString());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SplitPair<T> SplitLast<T>(this Span<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.LastIndexOf(separator);
return separatorIndex > -1
? new(source[..separatorIndex], source[(separatorIndex + 1)..])
: new(source, Span<T>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySplitPair<T> SplitLast<T>(this ReadOnlySpan<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.LastIndexOf(separator);
return separatorIndex > -1
? new(source[..separatorIndex], source[(separatorIndex + 1)..])
: new(source, ReadOnlySpan<T>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (Memory<T> Segment, Memory<T> Remainder) SplitLast<T>(this Memory<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.Span.LastIndexOf(separator);
return separatorIndex > -1
? (source[..separatorIndex], source[(separatorIndex + 1)..])
: (source, Memory<T>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (ReadOnlyMemory<T> Segment, ReadOnlyMemory<T> Remainder) SplitLast<T>(this ReadOnlyMemory<T> source, T separator)
where T : IEquatable<T>
{
var separatorIndex = source.Span.LastIndexOf(separator);
return separatorIndex > -1
? (source[..separatorIndex], source[(separatorIndex + 1)..])
: (source, ReadOnlyMemory<T>.Empty);
}
public readonly ref struct SplitPair<T>
{
public readonly Span<T> Segment;
public readonly Span<T> Remainder;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SplitPair(Span<T> segment, Span<T> remainder)
{
Segment = segment;
Remainder = remainder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out Span<T> segment, out Span<T> remainder)
{
segment = Segment;
remainder = Remainder;
}
}
public readonly ref struct ReadOnlySplitPair<T>
{
public readonly ReadOnlySpan<T> Segment;
public readonly ReadOnlySpan<T> Remainder;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySplitPair(ReadOnlySpan<T> segment, ReadOnlySpan<T> remainder)
{
Segment = segment;
Remainder = remainder;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out ReadOnlySpan<T> segment, out ReadOnlySpan<T> remainder)
{
segment = Segment;
remainder = Remainder;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment