Skip to content

Instantly share code, notes, and snippets.

@SolidAlloy
Created January 18, 2021 13:39
Show Gist options
  • Save SolidAlloy/85c3e30cd6fcfd3130c1dc02b28aa789 to your computer and use it in GitHub Desktop.
Save SolidAlloy/85c3e30cd6fcfd3130c1dc02b28aa789 to your computer and use it in GitHub Desktop.
ReadOnlySpan Split
using System;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
// Reduced-down and slightly refactored version of https://gist.github.com/LordJZ/92b7decebe52178a445a0b82f63e585a
// It exposes only (T separator) overload of the Split method which was enough for my needs.
public static class SpanExtensions
{
public readonly ref struct Enumerable<T>
where T : IEquatable<T>
{
private readonly ReadOnlySpan<T> _span;
private readonly T _separator;
public Enumerable(ReadOnlySpan<T> span, T separator)
{
_span = span;
_separator = separator;
}
[PublicAPI]
public Enumerator<T> GetEnumerator() => new Enumerator<T>(_span, _separator);
}
public ref struct Enumerator<T>
where T : IEquatable<T>
{
private const int SeparatorLength = 1;
private readonly ReadOnlySpan<T> _trailingEmptyItemSentinel;
private readonly T _separator;
private ReadOnlySpan<T> _span;
private ReadOnlySpan<T> _current;
public Enumerator(ReadOnlySpan<T> span, T separator)
{
_span = span;
_separator = separator;
_current = default;
_trailingEmptyItemSentinel = Unsafe.As<T[]>(nameof(_trailingEmptyItemSentinel)).AsSpan();
if (_span.IsEmpty)
TrailingEmptyItem = true;
}
[PublicAPI]
public ReadOnlySpan<T> Current => _current;
private bool TrailingEmptyItem
{
get => _span == _trailingEmptyItemSentinel;
set => _span = value ? _trailingEmptyItemSentinel : default;
}
[PublicAPI]
public bool MoveNext()
{
if (TrailingEmptyItem)
{
TrailingEmptyItem = false;
_current = default;
return true;
}
if (_span.IsEmpty)
{
_span = _current = default;
return false;
}
int idx = _span.IndexOf(_separator);
if (idx < 0)
{
_current = _span;
_span = default;
}
else
{
_current = _span.Slice(0, idx);
_span = _span.Slice(idx + SeparatorLength);
if (_span.IsEmpty)
TrailingEmptyItem = true;
}
return true;
}
}
[Pure, PublicAPI]
public static Enumerable<T> Split<T>(this ReadOnlySpan<T> span, T separator)
where T : IEquatable<T> => new Enumerable<T>(span, separator);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment