Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres not (yet) to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.5] - 2024-11-12

### Added

- 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.

### Changed

- 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.
- original Split methods are no longer available without passing span as a parameter.

## [1.4.2] - 2024-10-29

Expand Down
133 changes: 133 additions & 0 deletions src/Enumerators/System/SpanSplitEnumerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;

#if !NET9_0_OR_GREATER

namespace System
{
#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

/// <summary>
/// Gets the current element of the enumeration.
/// </summary>
/// <returns>Returns a <see cref="Range"/> instance that indicates the bounds of the current element withing the source span.</returns>
public Range Current { get; internal set; }

internal SpanSplitEnumerator(ReadOnlySpan<T> source, T delimiter)
{
Span = source;
Delimiter = delimiter;
Current = new Range(0, 0);
DelimiterSpan = default;
mode = SpanSplitEnumeratorMode.Delimiter;
}
internal SpanSplitEnumerator(ReadOnlySpan<T> source, ReadOnlySpan<T> delimiter, SpanSplitEnumeratorMode mode)
{
Span = source;
DelimiterSpan = delimiter;
Current = new Range(0, 0);
Delimiter = default!;
this.mode = mode;
}

#if NET8_0
internal SpanSplitEnumerator(ReadOnlySpan<T> source, SearchValues<T> searchValues)
{
Span = source;
Delimiter = default!;
SearchValues = searchValues;
Current = new Range(0, 0);
DelimiterSpan = default;
mode = SpanSplitEnumeratorMode.Delimiter;
}
#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[Current.Start..].IndexOf(Delimiter);
length = 1;
break;

case SpanSplitEnumeratorMode.Any:
index = Span[Current.Start..].IndexOfAny(DelimiterSpan);
length = 1;
break;

case SpanSplitEnumeratorMode.Sequence:
index = Span[Current.Start..].IndexOf(DelimiterSpan);
length = DelimiterSpan.Length;
break;

case SpanSplitEnumeratorMode.EmptySequence:
index = -1;
length = 1;
break;

#if NET8_0
case SpanSplitEnumeratorMode.SearchValues:
index = Span[Current.Start..].IndexOfAny(SearchValues);
length = 1;
break;
#endif
default:
return false;
}

if(index < 0)
{
Current = new Range(Span.Length, Span.Length);
mode = (SpanSplitEnumeratorMode)(-1);
return true;
}

Current = new Range(Current.End.Value + length, Current.Start.Value + index);

return true;
}
}

internal enum SpanSplitEnumeratorMode
{
Delimiter,
Any,
Sequence,
EmptySequence,
SearchValues
}
}
}

#endif
2 changes: 1 addition & 1 deletion src/ExceptionHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static ExceptionHelpers()
combination |= flag;
}

NegatedCombinationOfAllValidStringSplitOptions = (StringSplitOptions) ~combination;
NegatedCombinationOfAllValidStringSplitOptions = (StringSplitOptions)~combination;
}

internal static void ThrowIfGreaterThanOrEqual<T>(T value, T other,
Expand Down
74 changes: 74 additions & 0 deletions src/Extensions/ReadOnlySpan/Span/Split.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;

#if !NET9_0_OR_GREATER

namespace System
{
#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>
/// Returns a type that allows for enumeration of each element within a split span
/// using the provided separator character.
/// </summary>
/// <typeparam name="T">The type of the elements.</typeparam>
/// <param name="source">The source span to be enumerated.</param>
/// <param name="separator">The separator character to be used to split the provided span.</param>
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T separator) where T : IEquatable<T>
{
return new SpanSplitEnumerator<T>(source, separator);
}

/// <summary>
/// Returns a type that allows for enumeration of each element within a split span
/// using the provided separator span.
/// </summary>
/// <typeparam name="T">The type of the elements.</typeparam>
/// <param name="source">The source span to be enumerated.</param>
/// <param name="separator">The separator span to be used to split the provided span.</param>
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> separator) where T : IEquatable<T>
{
return new SpanSplitEnumerator<T>(source, separator, SpanSplitEnumeratorMode.Sequence);
}

/// <summary>
/// Returns a type that allows for enumeration of each element within a split span
/// using any of the provided elements.
/// </summary>
/// <typeparam name="T">The type of the elements.</typeparam>
/// <param name="source">The source span to be enumerated.</param>
/// <param name="separators">The separators to be used to split the provided span.</param>
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
public static SpanSplitEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> separators) where T : IEquatable<T>
{
return new SpanSplitEnumerator<T>(source, separators, SpanSplitEnumeratorMode.Any);
}

#if NET8_0
/// <summary>
/// Returns a type that allows for enumeration of each element within a split span
/// using the provided <see cref="SpanSplitEnumerator{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the elements.</typeparam>
/// <param name="source">The source span to be enumerated.</param>
/// <param name="separators">The <see cref="SpanSplitEnumerator{T}"/> to be used to split the provided span.</param>
/// <returns>Returns a <see cref="SpanSplitEnumerator{T}"/>.</returns>
/// <remarks>
/// Unlike <see cref="SplitAny{T}(ReadOnlySpan{T}, ReadOnlySpan{T})"/>, the <paramref name="separators"/> is not checked for being empty.
/// 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"/>.
/// </remarks>
public static SpanSplitEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, SearchValues<T> separators) where T : IEquatable<T>
{
return new SpanSplitEnumerator<T>(source, separators);
}
#endif

}
}

#endif
8 changes: 4 additions & 4 deletions src/Extensions/ReadOnlySpan/String/Split.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static partial class ReadOnlySpanExtensions
/// <param name="source">The <see cref="ReadOnlySpan{T}"/> to be split.</param>
/// <param name="delimiter">An instance of <typeparamref name="T"/> that delimits the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
/// <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>
public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T delimiter) where T : IEquatable<T>
public static SpanSplitEnumerator<T> Split<T>(ReadOnlySpan<T> source, T delimiter) where T : IEquatable<T>
{
return new SpanSplitEnumerator<T>(source, delimiter);
}
Expand All @@ -27,7 +27,7 @@ public static SpanSplitEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T del
/// <param name="count">The maximum number of sub-ReadOnlySpans to split into.</param>
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
/// <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>
public static SpanSplitWithCountEnumerator<T> Split<T>(this ReadOnlySpan<T> source, T delimiter, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
public static SpanSplitWithCountEnumerator<T> Split<T>(ReadOnlySpan<T> source, T delimiter, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
{
return new SpanSplitWithCountEnumerator<T>(source, delimiter, count, countExceedingBehaviour);
}
Expand All @@ -39,7 +39,7 @@ public static SpanSplitWithCountEnumerator<T> Split<T>(this ReadOnlySpan<T> sour
/// <param name="delimiter">A <see cref="char"/> that delimits the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
/// <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>
public static SpanSplitStringSplitOptionsEnumerator Split(this ReadOnlySpan<char> source, char delimiter, StringSplitOptions options)
public static SpanSplitStringSplitOptionsEnumerator Split(ReadOnlySpan<char> source, char delimiter, StringSplitOptions options)
{
return new SpanSplitStringSplitOptionsEnumerator(source, delimiter, options);
}
Expand All @@ -53,7 +53,7 @@ public static SpanSplitStringSplitOptionsEnumerator Split(this ReadOnlySpan<char
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
/// <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>
public static SpanSplitStringSplitOptionsWithCountEnumerator Split(this ReadOnlySpan<char> source, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
public static SpanSplitStringSplitOptionsWithCountEnumerator Split(ReadOnlySpan<char> source, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
{
return new SpanSplitStringSplitOptionsWithCountEnumerator(source, delimiter, count, options, countExceedingBehaviour);
}
Expand Down
8 changes: 4 additions & 4 deletions src/Extensions/ReadOnlySpan/String/SplitAny.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static partial class ReadOnlySpanExtensions
/// <param name="source">The <see cref="ReadOnlySpan{T}"/> to be split.</param>
/// <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>
/// <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>
public static SpanSplitAnyEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters) where T : IEquatable<T>
public static SpanSplitAnyEnumerator<T> SplitAny<T>(ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters) where T : IEquatable<T>
{
return new SpanSplitAnyEnumerator<T>(source, delimiters);
}
Expand All @@ -27,7 +27,7 @@ public static SpanSplitAnyEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source,
/// <param name="count">The maximum number of sub-ReadOnlySpans to split into.</param>
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
/// <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>
public static SpanSplitAnyWithCountEnumerator<T> SplitAny<T>(this ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
public static SpanSplitAnyWithCountEnumerator<T> SplitAny<T>(ReadOnlySpan<T> source, ReadOnlySpan<T> delimiters, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable<T>
{
return new SpanSplitAnyWithCountEnumerator<T>(source, delimiters, count, countExceedingBehaviour);
}
Expand All @@ -39,7 +39,7 @@ public static SpanSplitAnyWithCountEnumerator<T> SplitAny<T>(this ReadOnlySpan<T
/// <param name="delimiters">A <see cref="ReadOnlySpan{Char}"/>, that delimit the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
/// <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>
public static SpanSplitAnyStringSplitOptionsEnumerator SplitAny(this ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, StringSplitOptions options)
public static SpanSplitAnyStringSplitOptionsEnumerator SplitAny(ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, StringSplitOptions options)
{
return new SpanSplitAnyStringSplitOptionsEnumerator(source, delimiters, options);
}
Expand All @@ -53,7 +53,7 @@ public static SpanSplitAnyStringSplitOptionsEnumerator SplitAny(this ReadOnlySpa
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
/// <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>
public static SpanSplitAnyStringSplitOptionsWithCountEnumerator SplitAny(this ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
public static SpanSplitAnyStringSplitOptionsWithCountEnumerator SplitAny(ReadOnlySpan<char> source, ReadOnlySpan<char> delimiters, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
{
return new SpanSplitAnyStringSplitOptionsWithCountEnumerator(source, delimiters, count, options, countExceedingBehaviour);
}
Expand Down
Loading
Loading