Skip to content

Commit 56eab0b

Browse files
authored
Merge branch 'feature-Min-Max' into Span.Min
2 parents cffc31e + e9fdc1f commit 56eab0b

23 files changed

+920
-450
lines changed

.github/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# SpanExtensions
22

3+
[![NuGet Badge](https://buildstats.info/nuget/SpanExtensions.Net)](https://www.nuget.org/packages/SpanExtensions.Net)
4+
35
## About
46
**`ReadonlySpan<T>`** and **`Span<T>`** are great Types in _C#_, but unfortunately working with them can sometimes be sort of a hassle and some use cases seem straight up impossible, even though they are not.
57

@@ -9,8 +11,6 @@ Never again switch back to using `string` instead of `ReadonlySpan<T>`, just bec
911

1012
**SpanExtensions.Net** provides alternatives for many missing Extension Methods for **`ReadonlySpan<T>`** and **`Span<T>`**, ranging from `string.Split()` over `Enumerable.Skip()` and `Enumerable.Take()` to an improved `ReadOnlySpan<T>.IndexOf()`.
1113

12-
**SpanExtensions.Net** may also be found on [NuGet](https://www.nuget.org/packages/SpanExtensions.Net).
13-
1414
## Methods
1515
The following **Extension Methods** are contained:
1616

.github/workflows/dotnet.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
name: .NET
3+
4+
on:
5+
push:
6+
branches: [ "main" ]
7+
pull_request:
8+
branches: [ "dev" ]
9+
paths:
10+
- '**.cs'
11+
- '**.csproj'
12+
13+
jobs:
14+
build:
15+
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- uses: actions/checkout@v3
20+
- name: Setup .NET
21+
uses: actions/setup-dotnet@v3
22+
with:
23+
dotnet-version: 8.0.x
24+
- name: Restore dependencies
25+
run: dotnet restore
26+
- name: Build
27+
run: dotnet build --no-restore
28+
- name: Test
29+
run: dotnet test --no-build --verbosity normal

Changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ 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.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3] - 2024-1-25
9+
10+
### Changed
11+
12+
- documentation comments to better reflect the dotnet style (https://github.com/draconware-dev/SpanExtensions.Net/pull/8)
13+
814
## [1.2.1] - 2024-1-25
915

1016
### Fixed

src/CountExceedingBehaviour.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace SpanExtensions
2+
{
3+
/// <summary>
4+
/// Defines the behaviour of a split operation when there are more split instances than there may be.
5+
/// </summary>
6+
public enum CountExceedingBehaviour
7+
{
8+
/// <summary>
9+
/// The last element returned will be all the remaining elements appended as one.
10+
/// </summary>
11+
AppendLastElements,
12+
/// <summary>
13+
/// Every split instance more than permitted will not be returned.
14+
/// </summary>
15+
CutLastElements
16+
}
17+
}

src/Enumerators/Split/SpanSplitEnumerator.cs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,34 @@ namespace SpanExtensions.Enumerators
55
/// <summary>
66
/// Supports iteration over a <see cref="ReadOnlySpan{T}"/> by splitting it at a specified delimiter of type <typeparamref name="T"/>.
77
/// </summary>
8-
/// <typeparam name="T">The type of elements in the enumerated <see cref="ReadOnlySpan{T}"/></typeparam>
8+
/// <typeparam name="T">The type of elements in the enumerated <see cref="ReadOnlySpan{T}"/>.</typeparam>
99
public ref struct SpanSplitEnumerator<T> where T : IEquatable<T>
1010
{
1111
ReadOnlySpan<T> Span;
1212
readonly T Delimiter;
13+
bool enumerationDone;
1314

1415
/// <summary>
15-
/// Gets the element in the collection at the current position of the enumerator.
16+
/// Gets the element in the collection at the current position of the enumerator.
1617
/// </summary>
1718
public ReadOnlySpan<T> Current { get; internal set; }
1819

1920
/// <summary>
20-
/// Constructs a <see cref="SpanSplitEnumerator{T}"/> from a span and a delimiter. ONLY CONSUME THIS CLASS THROUGH <see cref="ReadOnlySpanExtensions.Split{T}(ReadOnlySpan{T}, T)"/>.
21+
/// Constructs a <see cref="SpanSplitEnumerator{T}"/> from a span and a delimiter. <strong>Only consume this class through <see cref="ReadOnlySpanExtensions.Split{T}(ReadOnlySpan{T}, T)"/></strong>.
2122
/// </summary>
22-
/// <param name="source">The <see cref="ReadOnlySpan{T}"/> to be split.</param>
23-
/// <param name="delimiter">An instance of <typeparamref name="T"/> that delimits the various sub-ReadOnlySpans in the <see cref="ReadOnlySpan{T}"/>.</param>
23+
/// <param name="source">The <see cref="ReadOnlySpan{T}"/> to be split.</param>
24+
/// <param name="delimiter">An instance of <typeparamref name="T"/> that delimits the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
2425
public SpanSplitEnumerator(ReadOnlySpan<T> source, T delimiter)
2526
{
2627
Span = source;
2728
Delimiter = delimiter;
2829
Current = default;
30+
enumerationDone = false;
2931
}
30-
/// <summary></summary>
32+
33+
/// <summary>
34+
/// Returns an enumerator that iterates through a collection.
35+
/// </summary>
3136
public readonly SpanSplitEnumerator<T> GetEnumerator()
3237
{
3338
return this;
@@ -36,19 +41,20 @@ public readonly SpanSplitEnumerator<T> GetEnumerator()
3641
/// <summary>
3742
/// Advances the enumerator to the next element of the collection.
3843
/// </summary>
39-
/// <returns><code>true</code> if the enumerator was successfully advanced to the next element; <code>false</code> if the enumerator has passed the end of the collection.</returns>
44+
/// <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>
4045
public bool MoveNext()
4146
{
42-
ReadOnlySpan<T> span = Span;
43-
if(span.IsEmpty)
47+
if(enumerationDone)
4448
{
4549
return false;
4650
}
51+
52+
ReadOnlySpan<T> span = Span;
4753
int index = span.IndexOf(Delimiter);
4854

4955
if(index == -1 || index >= span.Length)
5056
{
51-
Span = ReadOnlySpan<T>.Empty;
57+
enumerationDone = true;
5258
Current = span;
5359
return true;
5460
}

src/Enumerators/Split/SpanSplitStringSplitOptionsEnumerator.cs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,61 @@
22

33
namespace SpanExtensions.Enumerators
44
{
5-
/// <summary>
6-
/// Supports iteration over a <see cref="ReadOnlySpan{Char}"/> by splitting it at a specified delimiter and based on specified <see cref="StringSplitOptions"/>.
7-
/// </summary>
5+
/// <summary>
6+
/// Supports iteration over a <see cref="ReadOnlySpan{Char}"/> by splitting it at a specified delimiter and based on specified <see cref="StringSplitOptions"/>.
7+
/// </summary>
88
public ref struct SpanSplitStringSplitOptionsEnumerator
99
{
1010
ReadOnlySpan<char> Span;
1111
readonly char Delimiter;
1212
readonly StringSplitOptions Options;
13+
bool enumerationDone;
1314

1415
/// <summary>
15-
/// Gets the element in the collection at the current position of the enumerator.
16+
/// Gets the element in the collection at the current position of the enumerator.
1617
/// </summary>
1718
public ReadOnlySpan<char> Current { get; internal set; }
1819

1920
/// <summary>
20-
/// Constructs a <see cref="SpanSplitEnumerator{Char}"/> from a span and a delimiter. ONLY CONSUME THIS CLASS THROUGH <see cref="ReadOnlySpanExtensions.Split(ReadOnlySpan{char}, char, StringSplitOptions)"/>.
21+
/// Constructs a <see cref="SpanSplitStringSplitOptionsEnumerator"/> from a span and a delimiter. <strong>Only consume this class through <see cref="ReadOnlySpanExtensions.Split(ReadOnlySpan{char}, char, StringSplitOptions)"/></strong>.
2122
/// </summary>
22-
/// <param name="source">The <see cref="ReadOnlySpan{Char}"/> to be split.</param>
23-
/// <param name="delimiter">An <see cref="char"/> that delimits the various sub-ReadOnlySpans in the <see cref="ReadOnlySpan{Char}"/>.</param>
23+
/// <param name="source">The <see cref="ReadOnlySpan{Char}"/> to be split.</param>
24+
/// <param name="delimiter">A <see cref="char"/> that delimits the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
2425
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
2526
public SpanSplitStringSplitOptionsEnumerator(ReadOnlySpan<char> source, char delimiter, StringSplitOptions options)
2627
{
2728
Span = source;
2829
Delimiter = delimiter;
2930
Options = options;
3031
Current = default;
32+
enumerationDone = false;
3133
}
32-
/// <summary></summary>
34+
35+
/// <summary>
36+
/// Returns an enumerator that iterates through a collection.
37+
/// </summary>
3338
public readonly SpanSplitStringSplitOptionsEnumerator GetEnumerator()
3439
{
3540
return this;
3641
}
37-
42+
3843
/// <summary>
3944
/// Advances the enumerator to the next element of the collection.
4045
/// </summary>
41-
/// <returns><code>true</code> if the enumerator was successfully advanced to the next element; <code>false</code> if the enumerator has passed the end of the collection.</returns>
46+
/// <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>
4247
public bool MoveNext()
4348
{
44-
ReadOnlySpan<char> span = Span;
45-
if(span.IsEmpty)
49+
if(enumerationDone)
4650
{
4751
return false;
4852
}
53+
54+
ReadOnlySpan<char> span = Span;
4955
int index = span.IndexOf(Delimiter);
5056

5157
if(index == -1 || index >= span.Length)
5258
{
53-
Span = ReadOnlySpan<char>.Empty;
59+
enumerationDone = true;
5460
Current = span;
5561
return true;
5662
}
@@ -66,8 +72,20 @@ public bool MoveNext()
6672
if(Current.IsEmpty)
6773
{
6874
Span = span[(index + 1)..];
75+
if(Span.IsEmpty)
76+
{
77+
enumerationDone = true;
78+
return false;
79+
}
6980
return MoveNext();
7081
}
82+
83+
Span = span[(index + 1)..];
84+
if(Span.IsEmpty)
85+
{
86+
enumerationDone = true;
87+
}
88+
return true;
7189
}
7290
Span = span[(index + 1)..];
7391
return true;

src/Enumerators/Split/SpanSplitStringSplitOptionsWithCountEnumerator.cs

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,49 @@
22

33
namespace SpanExtensions.Enumerators
44
{
5-
/// <summary>
6-
/// Supports iteration over a <see cref="ReadOnlySpan{Char}"/> by splitting it at a specified delimiter and based on specified <see cref="StringSplitOptions"/> with an upper limit of splits performed.
7-
/// </summary>
5+
/// <summary>
6+
/// Supports iteration over a <see cref="ReadOnlySpan{Char}"/> by splitting it at a specified delimiter and based on specified <see cref="StringSplitOptions"/> with an upper limit of splits performed.
7+
/// </summary>
88
public ref struct SpanSplitStringSplitOptionsWithCountEnumerator
99
{
1010
ReadOnlySpan<char> Span;
1111
readonly char Delimiter;
1212
readonly StringSplitOptions Options;
1313
readonly int Count;
14+
readonly CountExceedingBehaviour CountExceedingBehaviour;
1415
int currentCount;
16+
bool enumerationDone;
17+
readonly int CountMinusOne;
1518

1619
/// <summary>
17-
/// Gets the element in the collection at the current position of the enumerator.
20+
/// Gets the element in the collection at the current position of the enumerator.
1821
/// </summary>
1922
public ReadOnlySpan<char> Current { get; internal set; }
2023

21-
public SpanSplitStringSplitOptionsWithCountEnumerator(ReadOnlySpan<char> source, char delimiter, StringSplitOptions options, int count)
24+
/// <summary>
25+
/// Constructs a <see cref="SpanSplitStringSplitOptionsWithCountEnumerator"/> from a span and a delimiter. <strong>Only consume this class through <see cref="ReadOnlySpanExtensions.Split(ReadOnlySpan{char}, char, int, StringSplitOptions, CountExceedingBehaviour)"/></strong>.
26+
/// </summary>
27+
/// <param name="source">The <see cref="ReadOnlySpan{Char}"/> to be split.</param>
28+
/// <param name="delimiter">A <see cref="char"/> that delimits the various sub-ReadOnlySpans in <paramref name="source"/>.</param>
29+
/// <param name="count">The maximum number of sub-ReadOnlySpans to split into.</param>
30+
/// <param name="options">A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.</param>
31+
/// <param name="countExceedingBehaviour">The handling of the instances more than count.</param>
32+
public SpanSplitStringSplitOptionsWithCountEnumerator(ReadOnlySpan<char> source, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendLastElements)
2233
{
2334
Span = source;
2435
Delimiter = delimiter;
25-
Options = options;
2636
Count = count;
37+
CountExceedingBehaviour = countExceedingBehaviour;
38+
Options = options;
2739
Current = default;
2840
currentCount = 0;
41+
enumerationDone = false;
42+
CountMinusOne = Math.Max(Count - 1, 0);
2943
}
30-
/// <summary></summary>
44+
45+
/// <summary>
46+
/// Returns an enumerator that iterates through a collection.
47+
/// </summary>
3148
public readonly SpanSplitStringSplitOptionsWithCountEnumerator GetEnumerator()
3249
{
3350
return this;
@@ -36,23 +53,44 @@ public readonly SpanSplitStringSplitOptionsWithCountEnumerator GetEnumerator()
3653
/// <summary>
3754
/// Advances the enumerator to the next element of the collection.
3855
/// </summary>
39-
/// <returns><code>true</code> if the enumerator was successfully advanced to the next element; <code>false</code> if the enumerator has passed the end of the collection.</returns>
56+
/// <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>
4057
public bool MoveNext()
4158
{
42-
ReadOnlySpan<char> span = Span;
43-
if(span.IsEmpty)
59+
if(enumerationDone)
4460
{
4561
return false;
4662
}
63+
64+
ReadOnlySpan<char> span = Span;
4765
if(currentCount == Count)
4866
{
4967
return false;
5068
}
5169
int index = span.IndexOf(Delimiter);
5270

71+
switch(CountExceedingBehaviour)
72+
{
73+
case CountExceedingBehaviour.CutLastElements:
74+
break;
75+
case CountExceedingBehaviour.AppendLastElements:
76+
if(currentCount == CountMinusOne)
77+
{
78+
ReadOnlySpan<char> lower = span[..index];
79+
ReadOnlySpan<char> upper = span[(index + 1)..];
80+
Span<char> temp = new char[lower.Length + upper.Length];
81+
lower.CopyTo(temp[..index]);
82+
upper.CopyTo(temp[index..]);
83+
Current = temp;
84+
currentCount++;
85+
return true;
86+
}
87+
break;
88+
default:
89+
throw new InvalidCountExceedingBehaviourException(CountExceedingBehaviour);
90+
}
5391
if(index == -1 || index >= span.Length)
5492
{
55-
Span = ReadOnlySpan<char>.Empty;
93+
enumerationDone = true;
5694
Current = span;
5795
return true;
5896
}
@@ -70,8 +108,20 @@ public bool MoveNext()
70108
if(Current.IsEmpty)
71109
{
72110
Span = span[(index + 1)..];
111+
if(Span.IsEmpty)
112+
{
113+
enumerationDone = true;
114+
return false;
115+
}
73116
return MoveNext();
74117
}
118+
119+
Span = span[(index + 1)..];
120+
if(Span.IsEmpty)
121+
{
122+
enumerationDone = true;
123+
}
124+
return true;
75125
}
76126
Span = span[(index + 1)..];
77127
return true;

0 commit comments

Comments
 (0)