-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathSpanSplitEnumerator.cs
More file actions
144 lines (128 loc) · 4.99 KB
/
SpanSplitEnumerator.cs
File metadata and controls
144 lines (128 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System;
using System.Buffers;
#if !NET9_0_OR_GREATER
namespace SpanExtensions
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static partial class MemoryExtensions
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
/// <summary>
/// Enables enumerating each split within a <see cref="ReadOnlySpan{T}"/> that has been divided using one or more separators.
/// </summary>
public ref struct SpanSplitEnumerator<T> where T : IEquatable<T>
{
readonly ReadOnlySpan<T> Span;
readonly T Delimiter;
readonly ReadOnlySpan<T> DelimiterSpan;
SpanSplitEnumeratorMode mode;
#if NET8_0
readonly SearchValues<T> SearchValues = null!;
#endif
int currentStartIndex;
int currentEndIndex;
int nextStartIndex;
/// <summary>
/// Gets the current element of the enumeration.
/// </summary>
/// <returns>Returns a <see cref="Range"/> instance that indicates the bounds of the current element within the source span.</returns>
public readonly Range Current => new Range(currentStartIndex, currentEndIndex);
internal SpanSplitEnumerator(ReadOnlySpan<T> source, T delimiter)
{
Span = source;
Delimiter = delimiter;
DelimiterSpan = default;
mode = SpanSplitEnumeratorMode.Delimiter;
currentStartIndex = 0;
currentEndIndex = 0;
nextStartIndex = 0;
}
internal SpanSplitEnumerator(ReadOnlySpan<T> source, ReadOnlySpan<T> delimiter, SpanSplitEnumeratorMode mode)
{
Span = source;
DelimiterSpan = delimiter;
Delimiter = default!;
this.mode = mode;
currentStartIndex = 0;
currentEndIndex = 0;
nextStartIndex = 0;
}
#if NET8_0
internal SpanSplitEnumerator(ReadOnlySpan<T> source, SearchValues<T> searchValues)
{
Span = source;
Delimiter = default!;
SearchValues = searchValues;
DelimiterSpan = default;
mode = SpanSplitEnumeratorMode.SearchValues;
currentStartIndex = 0;
currentEndIndex = 0;
nextStartIndex = 0;
}
#endif
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
public readonly SpanSplitEnumerator<T> GetEnumerator()
{
return this;
}
/// <summary>
/// Advances the enumerator to the next element of the collection.
/// </summary>
/// <returns><see langword="true"/> if the enumerator was successfully advanced to the next element; <see langword="false"/> if the enumerator has passed the end of the collection.</returns>
public bool MoveNext()
{
int index;
int length;
switch(mode)
{
case SpanSplitEnumeratorMode.Delimiter:
index = Span[nextStartIndex..].IndexOf(Delimiter);
length = 1;
break;
case SpanSplitEnumeratorMode.Any:
index = Span[nextStartIndex..].IndexOfAny(DelimiterSpan);
length = 1;
break;
case SpanSplitEnumeratorMode.Sequence:
index = Span[nextStartIndex..].IndexOf(DelimiterSpan);
length = DelimiterSpan.Length;
break;
case SpanSplitEnumeratorMode.EmptySequence:
index = -1;
length = 1;
break;
#if NET8_0
case SpanSplitEnumeratorMode.SearchValues:
index = Span[nextStartIndex..].IndexOfAny(SearchValues);
length = 1;
break;
#endif
default:
return false;
}
currentStartIndex = nextStartIndex;
if(index < 0)
{
currentEndIndex = Span.Length;
nextStartIndex = Span.Length;
mode = (SpanSplitEnumeratorMode)(-1);
return true;
}
currentEndIndex = currentStartIndex + index;
nextStartIndex = currentEndIndex + length;
return true;
}
}
internal enum SpanSplitEnumeratorMode
{
Delimiter,
Any,
Sequence,
EmptySequence,
SearchValues
}
}
}
#endif