Skip to content

Commit 344044b

Browse files
authored
Merge pull request #24 from draconware-dev/dev
Version 1.5
2 parents 7aa46a9 + 3b830d2 commit 344044b

30 files changed

+453
-167
lines changed

Changelog.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres not (yet) to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.5] - 2024-11-12
9+
10+
### Added
11+
12+
- implementations of the newly introduced Span.Split methods from .Net 9 for any version prior to .Net 9 to maintain backwards-compatibility across .Net versions.
13+
14+
### Changed
15+
16+
- Split extension methods to refer to new split implementations compatible to the ones in .Net 9 and made .Net 9 split methods the default from that version onwards. The original split methods are still accessible as static methods.
17+
- original Split methods are no longer available without passing span as a parameter.
818

919
## [1.4.2] - 2024-10-29
1020

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using System.Buffers;
2+
using System.Diagnostics;
3+
using System.Runtime.CompilerServices;
4+
5+
#if !NET9_0_OR_GREATER
6+
7+
namespace System
8+
{
9+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
10+
public static partial class MemoryExtensions
11+
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
12+
{
13+
/// <summary>
14+
/// Enables enumerating each split within a <see cref="ReadOnlySpan{T}"/> that has been divided using one or more separators.
15+
/// </summary>
16+
public ref struct SpanSplitEnumerator<T> where T : IEquatable<T>
17+
{
18+
readonly ReadOnlySpan<T> Span;
19+
readonly T Delimiter;
20+
readonly ReadOnlySpan<T> DelimiterSpan;
21+
SpanSplitEnumeratorMode mode;
22+
#if NET8_0
23+
readonly SearchValues<T> SearchValues = null!;
24+
#endif
25+
26+
/// <summary>
27+
/// Gets the current element of the enumeration.
28+
/// </summary>
29+
/// <returns>Returns a <see cref="Range"/> instance that indicates the bounds of the current element withing the source span.</returns>
30+
public Range Current { get; internal set; }
31+
32+
internal SpanSplitEnumerator(ReadOnlySpan<T> source, T delimiter)
33+
{
34+
Span = source;
35+
Delimiter = delimiter;
36+
Current = new Range(0, 0);
37+
DelimiterSpan = default;
38+
mode = SpanSplitEnumeratorMode.Delimiter;
39+
}
40+
internal SpanSplitEnumerator(ReadOnlySpan<T> source, ReadOnlySpan<T> delimiter, SpanSplitEnumeratorMode mode)
41+
{
42+
Span = source;
43+
DelimiterSpan = delimiter;
44+
Current = new Range(0, 0);
45+
Delimiter = default!;
46+
this.mode = mode;
47+
}
48+
49+
#if NET8_0
50+
internal SpanSplitEnumerator(ReadOnlySpan<T> source, SearchValues<T> searchValues)
51+
{
52+
Span = source;
53+
Delimiter = default!;
54+
SearchValues = searchValues;
55+
Current = new Range(0, 0);
56+
DelimiterSpan = default;
57+
mode = SpanSplitEnumeratorMode.Delimiter;
58+
}
59+
#endif
60+
/// <summary>
61+
/// Returns an enumerator that iterates through a collection.
62+
/// </summary>
63+
public readonly SpanSplitEnumerator<T> GetEnumerator()
64+
{
65+
return this;
66+
}
67+
68+
/// <summary>
69+
/// Advances the enumerator to the next element of the collection.
70+
/// </summary>
71+
/// <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>
72+
public bool MoveNext()
73+
{
74+
int index;
75+
int length;
76+
77+
switch(mode)
78+
{
79+
case SpanSplitEnumeratorMode.Delimiter:
80+
index = Span[Current.Start..].IndexOf(Delimiter);
81+
length = 1;
82+
break;
83+
84+
case SpanSplitEnumeratorMode.Any:
85+
index = Span[Current.Start..].IndexOfAny(DelimiterSpan);
86+
length = 1;
87+
break;
88+
89+
case SpanSplitEnumeratorMode.Sequence:
90+
index = Span[Current.Start..].IndexOf(DelimiterSpan);
91+
length = DelimiterSpan.Length;
92+
break;
93+
94+
case SpanSplitEnumeratorMode.EmptySequence:
95+
index = -1;
96+
length = 1;
97+
break;
98+
99+
#if NET8_0
100+
case SpanSplitEnumeratorMode.SearchValues:
101+
index = Span[Current.Start..].IndexOfAny(SearchValues);
102+
length = 1;
103+
break;
104+
#endif
105+
default:
106+
return false;
107+
}
108+
109+
if(index < 0)
110+
{
111+
Current = new Range(Span.Length, Span.Length);
112+
mode = (SpanSplitEnumeratorMode)(-1);
113+
return true;
114+
}
115+
116+
Current = new Range(Current.End.Value + length, Current.Start.Value + index);
117+
118+
return true;
119+
}
120+
}
121+
122+
internal enum SpanSplitEnumeratorMode
123+
{
124+
Delimiter,
125+
Any,
126+
Sequence,
127+
EmptySequence,
128+
SearchValues
129+
}
130+
}
131+
}
132+
133+
#endif

src/ExceptionHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ static ExceptionHelpers()
2323
combination |= flag;
2424
}
2525

26-
NegatedCombinationOfAllValidStringSplitOptions = (StringSplitOptions) ~combination;
26+
NegatedCombinationOfAllValidStringSplitOptions = (StringSplitOptions)~combination;
2727
}
2828

2929
internal static void ThrowIfGreaterThanOrEqual<T>(T value, T other,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System.Buffers;
2+
using System.Diagnostics;
3+
using System.Runtime.CompilerServices;
4+
5+
#if !NET9_0_OR_GREATER
6+
7+
namespace System
8+
{
9+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
10+
public static partial class MemoryExtensions
11+
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
12+
{
13+
/// <summary>
14+
/// Returns a type that allows for enumeration of each element within a split span
15+
/// using the provided separator character.
16+
/// </summary>
17+
/// <typeparam name="T">The type of the elements.</typeparam>
18+
/// <param name="source">The source span to be enumerated.</param>
19+
/// <param name="separator">The separator character to be used to split the provided span.</param>
20+
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
21+
public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T separator) where T : IEquatable<T>
22+
{
23+
return new SpanSplitEnumerator<T>(source, separator);
24+
}
25+
26+
/// <summary>
27+
/// Returns a type that allows for enumeration of each element within a split span
28+
/// using the provided separator span.
29+
/// </summary>
30+
/// <typeparam name="T">The type of the elements.</typeparam>
31+
/// <param name="source">The source span to be enumerated.</param>
32+
/// <param name="separator">The separator span to be used to split the provided span.</param>
33+
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
34+
public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> separator) where T : IEquatable<T>
35+
{
36+
return new SpanSplitEnumerator<T>(source, separator, SpanSplitEnumeratorMode.Sequence);
37+
}
38+
39+
/// <summary>
40+
/// Returns a type that allows for enumeration of each element within a split span
41+
/// using any of the provided elements.
42+
/// </summary>
43+
/// <typeparam name="T">The type of the elements.</typeparam>
44+
/// <param name="source">The source span to be enumerated.</param>
45+
/// <param name="separators">The separators to be used to split the provided span.</param>
46+
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
47+
public static SpanSplitEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> separators) where T : IEquatable<T>
48+
{
49+
return new SpanSplitEnumerator<T>(source, separators, SpanSplitEnumeratorMode.Any);
50+
}
51+
52+
#if NET8_0
53+
/// <summary>
54+
/// Returns a type that allows for enumeration of each element within a split span
55+
/// using the provided <see cref="SpanSplitEnumerator{T}"/>.
56+
/// </summary>
57+
/// <typeparam name="T">The type of the elements.</typeparam>
58+
/// <param name="source">The source span to be enumerated.</param>
59+
/// <param name="separators">The <see cref="SpanSplitEnumerator{T}"/> to be used to split the provided span.</param>
60+
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
61+
/// <remarks>
62+
/// Unlike <see cref="SplitAny{T}(ReadOnlySpan{T}, ReadOnlySpan{T})"/>, the <paramref name="separators"/> is not checked for being empty.
63+
/// An empty <paramref name="separators"/> will result in no separators being found, regardless of the type of <typeparamref name="T"/>, whereas <see cref="SplitAny{T}(ReadOnlySpan{T}, ReadOnlySpan{T})"/> will use all Unicode whitespace characters as separators if <paramref name="separators"/> is empty and <typeparamref name="T"/> is <see cref="char"/>.
64+
/// </remarks>
65+
public static SpanSplitEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, SearchValues<T> separators) where T : IEquatable<T>
66+
{
67+
return new SpanSplitEnumerator<T>(source, separators);
68+
}
69+
#endif
70+
71+
}
72+
}
73+
74+
#endif

src/Extensions/ReadOnlySpan/String/Split.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public static partial class ReadOnlySpanExtensions
1313
/// <param name="source">The <see cref="ReadOnlySpan{T}"/> to be split.</param>
1414
/// <param name="delimiter">An instance of <typeparamref name="T"/> that delimits the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
1515
/// <returns>An instance of the ref struct <see cref="SpanSplitEnumerator{T}"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
16-
public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T delimiter) where T : IEquatable<T>
16+
public static SpanSplitEnumerator<T> Split<T>(ReadOnlySpan<T> source, T delimiter) where T : IEquatable<T>
1717
{
1818
return new SpanSplitEnumerator<T>(source, delimiter);
1919
}
@@ -27,7 +27,7 @@ public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T del
2727
/// <param name="count">The maximum number of sub-ReadOnlySpans to split into.</param>
2828
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
2929
/// <returns>An instance of the ref struct <see cref="SpanSplitWithCountEnumerator{T}"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
30-
public static SpanSplitWithCountEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T delimiter, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
30+
public static SpanSplitWithCountEnumerator<T> Split<T>(ReadOnlySpan<T> source, T delimiter, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
3131
{
3232
return new SpanSplitWithCountEnumerator<T>(source, delimiter, count, countExceedingBehaviour);
3333
}
@@ -39,7 +39,7 @@ public static SpanSplitWithCountEnumerator<T> Split<T>(this ReadOnlySpan<T> sour
3939
/// <param name="delimiter">A <see cref="char"/> that delimits the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
4040
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
4141
/// <returns>An instance of the ref struct <see cref="SpanSplitStringSplitOptionsEnumerator"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
42-
public static SpanSplitStringSplitOptionsEnumerator Split(this ReadOnlySpan<char> source, char delimiter, StringSplitOptions options)
42+
public static SpanSplitStringSplitOptionsEnumerator Split(ReadOnlySpan<char> source, char delimiter, StringSplitOptions options)
4343
{
4444
return new SpanSplitStringSplitOptionsEnumerator(source, delimiter, options);
4545
}
@@ -53,7 +53,7 @@ public static SpanSplitStringSplitOptionsEnumerator Split(this ReadOnlySpan<char
5353
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
5454
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
5555
/// <returns>An instance of the ref struct <see cref="SpanSplitAnyStringSplitOptionsWithCountEnumerator"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
56-
public static SpanSplitStringSplitOptionsWithCountEnumerator Split(this ReadOnlySpan<char> source, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
56+
public static SpanSplitStringSplitOptionsWithCountEnumerator Split(ReadOnlySpan<char> source, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
5757
{
5858
return new SpanSplitStringSplitOptionsWithCountEnumerator(source, delimiter, count, options, countExceedingBehaviour);
5959
}

src/Extensions/ReadOnlySpan/String/SplitAny.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public static partial class ReadOnlySpanExtensions
1313
/// <param name="source">The <see cref="ReadOnlySpan{T}"/> to be split.</param>
1414
/// <param name="delimiters">A <see cref="ReadOnlySpan{T}"/> with the instances of <typeparamref name="T"/> that delimit the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
1515
/// <returns>An instance of the ref struct <see cref="SpanSplitAnyEnumerator{T}"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
16-
public static SpanSplitAnyEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters) where T : IEquatable<T>
16+
public static SpanSplitAnyEnumerator<T> SplitAny<T>(ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters) where T : IEquatable<T>
1717
{
1818
return new SpanSplitAnyEnumerator<T>(source, delimiters);
1919
}
@@ -27,7 +27,7 @@ public static SpanSplitAnyEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source,
2727
/// <param name="count">The maximum number of sub-ReadOnlySpans to split into.</param>
2828
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
2929
/// <returns>An instance of the ref struct <see cref="SpanSplitAnyWithCountEnumerator{T}"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
30-
public static SpanSplitAnyWithCountEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
30+
public static SpanSplitAnyWithCountEnumerator<T> SplitAny<T>(ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
3131
{
3232
return new SpanSplitAnyWithCountEnumerator<T>(source, delimiters, count, countExceedingBehaviour);
3333
}
@@ -39,7 +39,7 @@ public static SpanSplitAnyWithCountEnumerator<T> SplitAny<T>(this ReadOnlySpan<T
3939
/// <param name="delimiters">A <see cref="ReadOnlySpan{Char}"/>, that delimit the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
4040
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
4141
/// <returns>An instance of the ref struct <see cref="SpanSplitAnyStringSplitOptionsEnumerator"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
42-
public static SpanSplitAnyStringSplitOptionsEnumerator SplitAny(this ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, StringSplitOptions options)
42+
public static SpanSplitAnyStringSplitOptionsEnumerator SplitAny(ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, StringSplitOptions options)
4343
{
4444
return new SpanSplitAnyStringSplitOptionsEnumerator(source, delimiters, options);
4545
}
@@ -53,7 +53,7 @@ public static SpanSplitAnyStringSplitOptionsEnumerator SplitAny(this ReadOnlySpa
5353
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
5454
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
5555
/// <returns>An instance of the ref struct <see cref="SpanSplitAnyStringSplitOptionsWithCountEnumerator"/>, which works the same way as every <see cref="IEnumerator"/> does and can be used in a foreach construct.</returns>
56-
public static SpanSplitAnyStringSplitOptionsWithCountEnumerator SplitAny(this ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
56+
public static SpanSplitAnyStringSplitOptionsWithCountEnumerator SplitAny(ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
5757
{
5858
return new SpanSplitAnyStringSplitOptionsWithCountEnumerator(source, delimiters, count, options, countExceedingBehaviour);
5959
}

0 commit comments

Comments
 (0)