using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CodeWalker.Core.Utils { /// /// Enumerates the lines of a . /// /// /// To get an instance of this type, use . /// public ref struct SpanSplitEnumerator where T : IEquatable? { private ReadOnlySpan _remaining; private ReadOnlySpan _current; private bool _isEnumeratorActive; private readonly T _splitBy; private readonly ReadOnlySpan _splitBySpan; internal SpanSplitEnumerator(ReadOnlySpan buffer, T splitBy) { _remaining = buffer; _current = default; _isEnumeratorActive = true; _splitBy = splitBy; } /// /// Gets the line at the current position of the enumerator. /// public ReadOnlySpan Current => _current; /// /// Returns this instance as an enumerator. /// public readonly SpanSplitEnumerator GetEnumerator() => this; /// /// Advances the enumerator to the next line of the span. /// /// /// True if the enumerator successfully advanced to the next line; false if /// the enumerator has advanced past the end of the span. /// public bool MoveNext() { if (!_isEnumeratorActive) { return false; // EOF previously reached or enumerator was never initialized } ReadOnlySpan remaining = _remaining; int idx = remaining.IndexOf(_splitBy); if ((uint)idx < (uint)remaining.Length) { _current = remaining.Slice(0, idx); _remaining = remaining.Slice(idx + 1); } else { // We've reached EOF, but we still need to return 'true' for this final // iteration so that the caller can query the Current property once more. _current = remaining; _remaining = default; _isEnumeratorActive = false; } return true; } } public ref struct SpanSplitEnumeratorAny where T : IEquatable? { private ReadOnlySpan _remaining; private ReadOnlySpan _current; private bool _isEnumeratorActive; private readonly ReadOnlySpan _splitBy; internal SpanSplitEnumeratorAny(ReadOnlySpan buffer, ReadOnlySpan splitBy) { _remaining = buffer; _current = default; _isEnumeratorActive = true; _splitBy = splitBy; } /// /// Gets the line at the current position of the enumerator. /// public readonly ReadOnlySpan Current => _current; /// /// Returns this instance as an enumerator. /// public readonly SpanSplitEnumeratorAny GetEnumerator() => this; /// /// Advances the enumerator to the next line of the span. /// /// /// True if the enumerator successfully advanced to the next line; false if /// the enumerator has advanced past the end of the span. /// public bool MoveNext() { if (!_isEnumeratorActive) { return false; // EOF previously reached or enumerator was never initialized } ReadOnlySpan remaining = _remaining; int idx = remaining.IndexOfAny(_splitBy); if ((uint)idx < (uint)remaining.Length) { _current = remaining.Slice(0, idx); _remaining = remaining.Slice(idx + 1); } else { // We've reached EOF, but we still need to return 'true' for this final // iteration so that the caller can query the Current property once more. _current = remaining; _remaining = default; _isEnumeratorActive = false; } return true; } } public static class EnumerateSplitExtensions { public static SpanSplitEnumerator EnumerateSplit(this ReadOnlySpan span, T splitBy) where T : IEquatable { return new SpanSplitEnumerator(span, splitBy); } public static SpanSplitEnumeratorAny EnumerateSplitAny(this ReadOnlySpan span, ReadOnlySpan splitBy) where T : IEquatable { return new SpanSplitEnumeratorAny(span, splitBy); } public static SpanSplitEnumerator EnumerateSplit(this Span span, T splitBy) where T : IEquatable { return new SpanSplitEnumerator(span, splitBy); } public static SpanSplitEnumeratorAny EnumerateSplitAny(this Span span, ReadOnlySpan splitBy) where T : IEquatable { return new SpanSplitEnumeratorAny(span, splitBy); } public static SpanSplitEnumerator EnumerateSplit(this string str, char splitBy) { return EnumerateSplit(str.AsSpan(), splitBy); } public static SpanSplitEnumeratorAny EnumerateSplitAny(this string str, ReadOnlySpan splitBy) { return EnumerateSplitAny(str.AsSpan(), splitBy); } public static ReverseSpanSplitEnumerator ReverseEnumerateSplit(this ReadOnlySpan span, T splitBy) where T : IEquatable { return new ReverseSpanSplitEnumerator(span, splitBy); } public static ReverseSpanSplitEnumerator ReverseEnumerateSplit(this Span span, T splitBy) where T : IEquatable { return new ReverseSpanSplitEnumerator(span, splitBy); } public static ReverseSpanSplitEnumerator ReverseEnumerateSplit(this string str, char splitBy) { return ReverseEnumerateSplit(str.AsSpan(), splitBy); } } public ref struct ReverseSpanSplitEnumerator where T : IEquatable? { private ReadOnlySpan _remaining; private ReadOnlySpan _current; private bool _isEnumeratorActive; private T _splitBy; internal ReverseSpanSplitEnumerator(ReadOnlySpan buffer, T splitBy) { _remaining = buffer; _current = default; _isEnumeratorActive = true; _splitBy = splitBy; } /// /// Gets the line at the current position of the enumerator. /// public readonly ReadOnlySpan Current => _current; /// /// Returns this instance as an enumerator. /// public readonly ReverseSpanSplitEnumerator GetEnumerator() => this; /// /// Advances the enumerator to the next line of the span. /// /// /// True if the enumerator successfully advanced to the next line; false if /// the enumerator has advanced past the end of the span. /// public bool MoveNext() { if (!_isEnumeratorActive) { return false; // EOF previously reached or enumerator was never initialized } ReadOnlySpan remaining = _remaining; int idx = remaining.LastIndexOf(_splitBy); if ((uint)idx < (uint)remaining.Length) { _current = remaining.Slice(idx + 1); _remaining = remaining.Slice(0, idx); } else { // We've reached EOF, but we still need to return 'true' for this final // iteration so that the caller can query the Current property once more. _current = remaining; _remaining = default; _isEnumeratorActive = false; } return true; } } }