Skip to content

Commit a67eaa7

Browse files
committed
fixed bugs in Split
1 parent 373d500 commit a67eaa7

File tree

9 files changed

+80
-18
lines changed

9 files changed

+80
-18
lines changed

src/Enumerators/System/SpanSplitEnumerator.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
using System;
22
using System.Buffers;
3-
using System.Diagnostics;
4-
using System.Runtime.CompilerServices;
53

64
#if !NET9_0_OR_GREATER
75

@@ -62,7 +60,7 @@ internal SpanSplitEnumerator(ReadOnlySpan<T> source, SearchValues<T> searchValue
6260
Delimiter = default!;
6361
SearchValues = searchValues;
6462
DelimiterSpan = default;
65-
mode = SpanSplitEnumeratorMode.Delimiter;
63+
mode = SpanSplitEnumeratorMode.SearchValues;
6664
currentStartIndex = 0;
6765
currentEndIndex = 0;
6866
nextStartIndex = 0;
@@ -118,19 +116,19 @@ public bool MoveNext()
118116
}
119117

120118
currentStartIndex = nextStartIndex;
121-
119+
122120
if(index < 0)
123121
{
124122
currentEndIndex = Span.Length;
125123
nextStartIndex = Span.Length;
126-
124+
127125
mode = (SpanSplitEnumeratorMode)(-1);
128126
return true;
129127
}
130128

131129
currentEndIndex = currentStartIndex + index;
132130
nextStartIndex = currentEndIndex + length;
133-
131+
134132
return true;
135133
}
136134
}

src/ExceptionHelpers.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Diagnostics.CodeAnalysis;
3-
using System.Globalization;
43
using System.Numerics;
54
using System.Runtime.CompilerServices;
65
using SpanExtensions;

src/Extensions/ReadOnlySpan/Span/Split.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
using System;
22
using System.Buffers;
3-
using System.Diagnostics;
43
using System.Runtime.CompilerServices;
4+
using System.Runtime.InteropServices;
55

66
#if !NET9_0_OR_GREATER
77

88
namespace SpanExtensions
99
{
10+
1011
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
1112
public static partial class MemoryExtensions
1213
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
1314
{
15+
static readonly char[] WhiteSpaceDelimiters = new char[] { ' ', '\t', '\n', '\v', '\f', '\r', '\u0085', '\u00A0', '\u1680',
16+
'\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006', '\u2007', '\u2008', '\u2009',
17+
'\u200A', '\u2028', '\u2029', '\u202F', '\u205F', '\u3000' };
18+
#if NET8_0
19+
static readonly SearchValues<char> WhiteSpaceSearchValues = SearchValues.Create(WhiteSpaceDelimiters);
20+
#endif
21+
1422
/// <summary>
1523
/// Returns a type that allows for enumeration of each element within a split span
1624
/// using the provided separator character.
@@ -47,6 +55,25 @@ public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, ReadO
4755
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
4856
public static SpanSplitEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> separators) where T : IEquatable<T>
4957
{
58+
if(separators.Length == 0 && typeof(T) == typeof(char))
59+
{
60+
#if NET8_0
61+
return new SpanSplitEnumerator<T>(source, Unsafe.As<SearchValues<T>>(WhiteSpaceSearchValues));
62+
#elif NET5_0_OR_GREATER
63+
ref char data = ref MemoryMarshal.GetArrayDataReference(WhiteSpaceDelimiters);
64+
ref T convertedData = ref Unsafe.As<char, T>(ref data);
65+
separators = MemoryMarshal.CreateReadOnlySpan(ref convertedData, WhiteSpaceDelimiters.Length);
66+
#else
67+
unsafe
68+
{
69+
fixed(char* ptr = &WhiteSpaceDelimiters[0])
70+
{
71+
separators = new ReadOnlySpan<T>(ptr, WhiteSpaceDelimiters.Length);
72+
}
73+
}
74+
#endif
75+
}
76+
5077
return new SpanSplitEnumerator<T>(source, separators, SpanSplitEnumeratorMode.Any);
5178
}
5279

src/Extensions/Span/Linq/First.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Reflection;
32

43
namespace SpanExtensions
54
{

src/Extensions/Span/Span/Split.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
using System;
22
using System.Buffers;
3-
using System.Diagnostics;
4-
using System.Runtime.CompilerServices;
53

64
#if !NET9_0_OR_GREATER
75

@@ -21,7 +19,7 @@ public static partial class MemoryExtensions
2119
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
2220
public static SpanSplitEnumerator<T> Split<T>(this Span<T> source, T separator) where T : IEquatable<T>
2321
{
24-
return new SpanSplitEnumerator<T>(source, separator);
22+
return Split((ReadOnlySpan<T>)source, separator);
2523
}
2624

2725
/// <summary>
@@ -34,7 +32,7 @@ public static SpanSplitEnumerator<T> Split<T>(this Span<T> source, T separator)
3432
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
3533
public static SpanSplitEnumerator<T> Split<T>(this Span<T> source, ReadOnlySpan<T> separator) where T : IEquatable<T>
3634
{
37-
return new SpanSplitEnumerator<T>(source, separator, SpanSplitEnumeratorMode.Sequence);
35+
return Split((ReadOnlySpan<T>)source, separator);
3836
}
3937

4038
/// <summary>
@@ -47,7 +45,7 @@ public static SpanSplitEnumerator<T> Split<T>(this Span<T> source, ReadOnlySpan<
4745
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
4846
public static SpanSplitEnumerator<T> SplitAny<T>(this Span<T> source, ReadOnlySpan<T> separators) where T : IEquatable<T>
4947
{
50-
return new SpanSplitEnumerator<T>(source, separators, SpanSplitEnumeratorMode.Any);
48+
return SplitAny((ReadOnlySpan<T>)source, separators);
5149
}
5250

5351
#if NET8_0
@@ -65,7 +63,7 @@ public static SpanSplitEnumerator<T> SplitAny<T>(this Span<T> source, ReadOnlySp
6563
/// </remarks>
6664
public static SpanSplitEnumerator<T> SplitAny<T>(this Span<T> source, SearchValues<T> separators) where T : IEquatable<T>
6765
{
68-
return new SpanSplitEnumerator<T>(source, separators);
66+
return SplitAny((ReadOnlySpan<T>)source, separators);
6967
}
7068
#endif
7169

src/SpanExtensions.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<PackageId>SpanExtensions.Net</PackageId>
2727
<PackageReadmeFile>README.md</PackageReadmeFile>
2828
<PackageIcon>icon.png</PackageIcon>
29+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
2930
</PropertyGroup>
3031

3132
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny/Tests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,31 @@ public void UndefinedCountExceedingBehaviourOptionThrowsArgumentException()
148148
{
149149
Assert.Throws<ArgumentException>(() => ReadOnlySpanExtensions.SplitAny("aabb".AsSpan(), ['b', 'c'], 1, InvalidCountExceedingBehaviour));
150150
}
151+
152+
[Fact]
153+
public void EmptyDelimitersUsesWhiteSpaceCharacters()
154+
{
155+
ReadOnlySpan<char> source = "Hello World!\nThis is a test.\rLet's see how it works.";
156+
ReadOnlySpan<char> delimiters = "";
157+
var expected = new[]
158+
{
159+
"Hello".ToCharArray(),
160+
"World!".ToCharArray(),
161+
"This".ToCharArray(),
162+
"is".ToCharArray(),
163+
"a".ToCharArray(),
164+
"test.".ToCharArray(),
165+
"Let's".ToCharArray(),
166+
"see".ToCharArray(),
167+
"how".ToCharArray(),
168+
"it".ToCharArray(),
169+
"works.".ToCharArray()
170+
};
171+
172+
var actual = source.SplitAny(delimiters).ToSystemEnumerable(source);
173+
174+
Assert.Equal(expected, actual);
175+
}
151176
}
152177
}
153178
}

tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny_StringSplitOptions/Data.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using static SpanExtensions.Tests.UnitTests.TestHelper;
2-
3-
namespace SpanExtensions.Tests.UnitTests
1+
namespace SpanExtensions.Tests.UnitTests
42
{
53
public static partial class ReadOnlySpanSplitTests
64
{

tests/unit-tests/ToSystemEnumerableExtensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,22 @@ public static IEnumerable<IEnumerable<char>> ToSystemEnumerable(this SpanSplitSe
211211

212212
return list;
213213
}
214+
215+
public static IEnumerable<IEnumerable<T>> ToSystemEnumerable<T>(this MemoryExtensions.SpanSplitEnumerator<T> spanEnumerator, ReadOnlySpan<T> span, int maxCount = 100) where T : IEquatable<T>
216+
{
217+
List<T[]> list = [];
218+
219+
foreach(Range range in spanEnumerator)
220+
{
221+
list.Add(span[range].ToArray());
222+
223+
if(list.Count >= maxCount)
224+
{
225+
throw new IndexOutOfRangeException($"Enumeration exceeded {maxCount}.");
226+
}
227+
}
228+
229+
return list;
230+
}
214231
}
215232
}

0 commit comments

Comments
 (0)