diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 4f9d42b..4ea7055 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -26,4 +26,4 @@ jobs:
- name: Build
run: dotnet build --no-restore
- name: Test
- run: dotnet test --no-build --verbosity normal
+ run: dotnet test --no-build --verbosity normal --filter FullyQualifiedName!~SpanExtensions.Tests.Fuzzing
diff --git a/SpanExtensions.sln b/SpanExtensions.sln
index f44a04a..3b36ef6 100644
--- a/SpanExtensions.sln
+++ b/SpanExtensions.sln
@@ -5,6 +5,10 @@ VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpanExtensions", "src\SpanExtensions.csproj", "{75DE5AFD-663E-415D-9B95-6BC513BD4A07}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "tests\unit-tests\UnitTests.csproj", "{B48A0293-A7FF-4E39-8F8D-57B6F824D70F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FuzzingTests", "tests\fuzzing\FuzzingTests.csproj", "{63CA0F05-0019-4ED3-AD94-45A5CE4D338F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +19,14 @@ Global
{75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {63CA0F05-0019-4ED3-AD94-45A5CE4D338F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {63CA0F05-0019-4ED3-AD94-45A5CE4D338F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {63CA0F05-0019-4ED3-AD94-45A5CE4D338F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {63CA0F05-0019-4ED3-AD94-45A5CE4D338F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Enumerators/Split/SpanSplitWithCountEnumerator.cs b/src/Enumerators/Split/SpanSplitWithCountEnumerator.cs
index e65a913..203ba5a 100644
--- a/src/Enumerators/Split/SpanSplitWithCountEnumerator.cs
+++ b/src/Enumerators/Split/SpanSplitWithCountEnumerator.cs
@@ -6,12 +6,12 @@ namespace SpanExtensions.Enumerators
/// Supports iteration over a by splitting it a a specified delimiter of type with an upper limit of splits performed.
///
/// The type of elements in the enumerated .
- public ref struct SpanSplitWithCountEnumerator where T : IEquatable
+ public ref struct SpanSplitWithCountEnumerator where T : IEquatable
{
ReadOnlySpan Span;
readonly T Delimiter;
readonly int Count;
- readonly CountExceedingBehaviour CountExceedingBehaviour;
+ readonly CountExceedingBehaviour CountExceedingBehaviour;
int currentCount;
bool enumerationDone;
readonly int CountMinusOne;
@@ -68,18 +68,18 @@ public bool MoveNext()
switch(CountExceedingBehaviour)
{
case CountExceedingBehaviour.CutLastElements:
- break;
+ break;
case CountExceedingBehaviour.AppendLastElements:
- if(currentCount == CountMinusOne)
+ if(currentCount == CountMinusOne)
{
- ReadOnlySpan lower = span[..index];
- ReadOnlySpan upper = span[(index + 1)..];
- Span temp = new T[lower.Length + upper.Length];
- lower.CopyTo(temp[..index]);
+ ReadOnlySpan lower = span[..index];
+ ReadOnlySpan upper = span[(index + 1)..];
+ Span temp = new T[lower.Length + upper.Length];
+ lower.CopyTo(temp[..index]);
upper.CopyTo(temp[index..]);
- Current = temp;
+ Current = temp;
currentCount++;
- return true;
+ return true;
}
break;
default:
diff --git a/src/Extensions/ReadOnlySpan/ReadOnlySpanExtensions.Linq.cs b/src/Extensions/ReadOnlySpan/ReadOnlySpanExtensions.Linq.cs
index e250794..f3247ed 100644
--- a/src/Extensions/ReadOnlySpan/ReadOnlySpanExtensions.Linq.cs
+++ b/src/Extensions/ReadOnlySpan/ReadOnlySpanExtensions.Linq.cs
@@ -315,9 +315,9 @@ public static ReadOnlySpan Take(this ReadOnlySpan source, int count)
public static ReadOnlySpan SkipWhile(this ReadOnlySpan source, Predicate condition)
{
int count = 0;
- while (count < source.Length)
+ while(count < source.Length)
{
- T t = source[count];
+ T t = source[count];
if(!condition(t))
{
return source.Skip(count);
diff --git a/src/Extensions/Span/SpanExtensions.Split.cs b/src/Extensions/Span/SpanExtensions.Split.cs
index 6a2f427..31df39a 100644
--- a/src/Extensions/Span/SpanExtensions.Split.cs
+++ b/src/Extensions/Span/SpanExtensions.Split.cs
@@ -56,7 +56,7 @@ public static SpanSplitStringSplitOptionsEnumerator Split(this Span source
/// A bitwise combination of the enumeration values that specifies whether to trim results and include empty results.
/// The handling of the instances more than count.
/// An instance of the ref struct , which works the same way as every does and can be used in a foreach construct.
- public static SpanSplitStringSplitOptionsWithCountEnumerator Split(this Span source, char delimiter, StringSplitOptions options, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendLastElements)
+ public static SpanSplitStringSplitOptionsWithCountEnumerator Split(this Span source, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendLastElements)
{
return new SpanSplitStringSplitOptionsWithCountEnumerator(source, delimiter, count, options, countExceedingBehaviour);
}
@@ -108,7 +108,7 @@ public static SpanSplitAnyStringSplitOptionsEnumerator SplitAny(this Span
/// The maximum number of sub-ReadOnlySpans to split into.
/// The handling of the instances more than count.
/// An instance of the ref struct , which works the same way as every does and can be used in a foreach construct.
- public static SpanSplitAnyStringSplitOptionsWithCountEnumerator SplitAny(this Span source, ReadOnlySpan delimiters, StringSplitOptions options, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendLastElements)
+ public static SpanSplitAnyStringSplitOptionsWithCountEnumerator SplitAny(this Span source, ReadOnlySpan delimiters, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendLastElements)
{
return new SpanSplitAnyStringSplitOptionsWithCountEnumerator(source, delimiters, count, options, countExceedingBehaviour);
}
@@ -160,7 +160,7 @@ public static SpanSplitSequenceStringSplitOptionsEnumerator Split(this SpanA bitwise combination of the enumeration values that specifies whether to trim results and include empty results.
/// The handling of the instances more than count.
/// An instance of the ref struct , which works the same way as every does and can be used in a foreach construct.
- public static SpanSplitSequenceStringSplitOptionsWithCountEnumerator Split(this Span source, ReadOnlySpan delimiter, StringSplitOptions options, int count, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendLastElements)
+ public static SpanSplitSequenceStringSplitOptionsWithCountEnumerator Split(this Span source, ReadOnlySpan delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendLastElements)
{
return new SpanSplitSequenceStringSplitOptionsWithCountEnumerator(source, delimiter, count, options, countExceedingBehaviour);
}
diff --git a/src/InvalidCountExceedingBehaviourException.cs b/src/InvalidCountExceedingBehaviourException.cs
index 75eed7e..20fa89f 100644
--- a/src/InvalidCountExceedingBehaviourException.cs
+++ b/src/InvalidCountExceedingBehaviourException.cs
@@ -13,9 +13,9 @@ public class InvalidCountExceedingBehaviourException : Exception
///
/// The invalid .
public InvalidCountExceedingBehaviourException(CountExceedingBehaviour countExceedingBehaviour) :
- base($"CountExceedingBehaviour with ID '{(int) countExceedingBehaviour} is not defined. CountExceedingBehaviour only defines {GetCountExceedingBehaviourNamesListed()}.")
+ base($"CountExceedingBehaviour with ID '{(int)countExceedingBehaviour} is not defined. CountExceedingBehaviour only defines {GetCountExceedingBehaviourNamesListed()}.")
{
-
+
}
static string GetCountExceedingBehaviourNamesListed()
@@ -24,7 +24,7 @@ static string GetCountExceedingBehaviourNamesListed()
#if NET5_0_OR_GREATER
countExceedingBehaviourNames = Enum.GetNames();
#else
- countExceedingBehaviourNames = (string[]) Enum.GetNames(typeof(CountExceedingBehaviour));
+ countExceedingBehaviourNames = (string[])Enum.GetNames(typeof(CountExceedingBehaviour));
#endif
switch(countExceedingBehaviourNames.Length)
{
@@ -33,7 +33,7 @@ static string GetCountExceedingBehaviourNamesListed()
case 1:
return countExceedingBehaviourNames[0];
default:
- string first = countExceedingBehaviourNames[0];
+ string first = countExceedingBehaviourNames[0];
string end = string.Join(',', countExceedingBehaviourNames, 1, countExceedingBehaviourNames.Length - 1);
return $"{first} and {end}";
}
diff --git a/src/SpanExtensions.csproj b/src/SpanExtensions.csproj
index 15337b4..951e1e2 100644
--- a/src/SpanExtensions.csproj
+++ b/src/SpanExtensions.csproj
@@ -2,7 +2,7 @@
net8.0;net7.0;net6.0;net5.0;netstandard2.1
- disable
+ disable
enable
True
AnyCPU
diff --git a/tests/fuzzing/FuzzingTests.csproj b/tests/fuzzing/FuzzingTests.csproj
new file mode 100644
index 0000000..f810771
--- /dev/null
+++ b/tests/fuzzing/FuzzingTests.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net8.0;net7.0;net6.0;net5.0
+ latest
+ enable
+ enable
+
+ false
+ true
+
+ SpanExtensions.Tests.Fuzzing
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/tests/fuzzing/GlobalUsings.cs b/tests/fuzzing/GlobalUsings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/tests/fuzzing/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/tests/fuzzing/TestHelper.cs b/tests/fuzzing/TestHelper.cs
new file mode 100644
index 0000000..76ee164
--- /dev/null
+++ b/tests/fuzzing/TestHelper.cs
@@ -0,0 +1,812 @@
+using SpanExtensions;
+using System.Collections;
+using System.Diagnostics;
+using System.Globalization;
+using System.Text;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static class TestHelper
+ {
+ public static readonly Random random = new();
+
+ ///
+ /// Generates a sequence of a specified number of random integers that are in the specified range.
+ ///
+ /// The number of integers to generate.
+ /// The inclusive lower bound of the random numbers returned.
+ /// The exclusive upper bound of the random number returned. must be greater than or equal to .
+ /// A sequence of random integers.
+ public static IEnumerable GenerateRandomIntegers(int count, int minValue, int maxValue)
+ {
+ for(int i = 0; i < count; i++)
+ {
+ yield return random.Next(minValue, maxValue);
+ }
+ }
+
+ ///
+ /// Generates a random string of a specified length. The string is generated from an alphabet of lowercase letters, numbers, and white spaces.
+ ///
+ /// The length of the generated string.
+ /// A random string.
+ public static string GenerateRandomString(int length)
+ {
+ const string alphabet = "abcdefghijklmnopqrstuvwxyz0123456789 ";
+
+ StringBuilder builder = new(length);
+ for(int i = 0; i < length; i++)
+ {
+ int alphabetIndex = random.Next(alphabet.Length);
+ builder.Append(alphabet[alphabetIndex]);
+ }
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Get a random element from the specified sequence, or a specified default value if the source is empty.
+ ///
+ /// The type of the sequence array.
+ /// The target sequence.
+ /// The value to return if is empty.
+ /// A random element from , or if is empty.
+ public static T RandomElementOrDefault(this ReadOnlySpan source, T @default = default) where T : struct
+ {
+ return !source.IsEmpty ? source[random.Next(source.Length)] : @default;
+ }
+
+ ///
+ /// Get a random element from the specified array, or a specified default value if the array is empty.
+ ///
+ /// The type of the target array.
+ /// The target array.
+ /// The value to return if is empty.
+ /// A random element from , or if the array is empty.
+ public static T RandomElementOrDefault(this T[] array, T @default = default) where T : struct
+ {
+ return RandomElementOrDefault(array.AsReadOnlySpan(), @default);
+ }
+
+ ///
+ /// Get a random element from the specified string, or a specified default character if the string is empty.
+ ///
+ /// The target string.
+ /// The value to return if is empty.
+ /// A random element from , or if is empty.
+ public static char RandomElementOrDefault(this string @string, char @default = default)
+ {
+ return RandomElementOrDefault(@string.AsSpan(), @default);
+ }
+
+ ///
+ /// Get a random element subsequence of the specified length from the specified sequence,
+ /// or a specified default sequence if the source is empty or shorter than the specified length.
+ ///
+ /// The type of the target sequence.
+ /// The target sequence.
+ /// The length of the subsequence.
+ /// The value to return if is empty or shorter than .
+ /// A random subsequence of length , or if is empty or shorted than that length.
+ /// If is negative.
+ /// If the length of did not match .
+ public static T[] RandomSubsequenceOrDefault(this ReadOnlySpan source, int length, T[]? @default = null) where T : struct
+ {
+ if(length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), "Can't be negative.");
+ }
+ if(@default != null && @default.Length != length)
+ {
+ throw new ArgumentException("The length must match.", nameof(@default));
+ }
+
+ if(source.Length < length)
+ {
+ return @default ?? new T[length];
+ }
+
+ int startIndex = random.Next(source.Length - length);
+ return source[startIndex..(startIndex + length)].ToArray();
+ }
+
+ ///
+ /// Get a random element subsequence of the specified length from the specified array,
+ /// or a specified default sequence if the source is empty or shorter than the specified length.
+ ///
+ /// The type of the target array.
+ /// The target array.
+ /// The length of the subsequence.
+ /// The value to return if is empty or shorter than .
+ /// A random subsequence of length , or if the array is empty or shorted than that length.
+ /// If is negative.
+ /// If the length of did not match .
+ public static T[] RandomSubsequenceOrDefault(this T[] array, int length, T[]? @default = null) where T : struct
+ {
+ return RandomSubsequenceOrDefault(array.AsReadOnlySpan(), length, @default);
+ }
+
+ ///
+ /// Get a random element subsequence of the specified length from the specified string,
+ /// or a specified default sequence if the source is empty or shorter than the specified length.
+ ///
+ /// The target string.
+ /// The length of the string.
+ /// The value to return if is empty or shorter than .
+ /// A random subsequence of length , or if is empty or shorted than that length.
+ /// If is negative.
+ /// If the length of did not match .
+ public static char[] RandomSubsequenceOrDefault(this string @string, int length, char[]? @default = null)
+ {
+ return RandomSubsequenceOrDefault(@string.AsSpan(), length, @default);
+ }
+
+ ///
+ /// Replaces a random element in the array with the specified replacement.
+ ///
+ /// The type of the target array.
+ /// The target array.
+ /// The element to replace with.
+ /// A copy of with the element at a random position replaced with .
+ public static T[] ReplaceRandomElement(this T[] source, T replacement)
+ {
+ T[] copy = new T[source.Length];
+ source.CopyTo(copy, 0);
+
+ if(source.Length != 0)
+ {
+ copy[random.Next(source.Length)] = replacement;
+ }
+
+ return copy;
+ }
+
+ ///
+ /// Replaces the element in the specified position with the specified replacement.
+ ///
+ /// The type of the target array.
+ /// The target array.
+ /// The position of the element to replace with.
+ /// The element to replace with.
+ /// A copy of with the element at replaced with .
+ public static T[] ReplaceAt(this T[] source, int position, T replacement)
+ {
+ T[] copy = new T[source.Length];
+ source.CopyTo(copy, 0);
+ copy[position] = replacement;
+ return copy;
+ }
+
+ ///
+ /// An alternative to that multiplies the current element by a multiplier every time.
+ ///
+ /// The first element.
+ /// The maximum the last element can be.
+ /// The multiplier.
+ /// If isn't positive, or if or is negative.
+ public sealed class MultiplierRange(int start, int max, int multiplier) : IEnumerable
+ {
+ public IEnumerator GetEnumerator()
+ {
+ if(start <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(start), "Value must be positive.");
+ }
+ if(max < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(max), "Value can't be negative.");
+ }
+ if(multiplier < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(multiplier), "Value can't be negative.");
+ }
+
+ static IEnumerator Iterate(int start, int max, int multiplier)
+ {
+ for(int i = start; i <= max; i *= multiplier)
+ {
+ yield return i;
+ }
+ }
+
+ return Iterate(start, max, multiplier);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+
+ ///
+ /// Iterates over two sequence is order.
+ ///
+ /// The type of the elements.
+ /// The first sequence to iterate.
+ /// THe second sequence ot iterate.
+ /// An that iterates over the two sequences in order.
+ public static IEnumerable And(this IEnumerable first, IEnumerable second)
+ {
+ foreach(T item in first)
+ {
+ yield return item;
+ }
+ foreach(T item in second)
+ {
+ yield return item;
+ }
+ }
+
+ ///
+ /// Generates a message to be displayed when an assertion fails containing the necessary information to reproduce the failure.
+ ///
+ /// The element type of the source span.
+ /// The elements inside the source span.
+ /// The expected results.
+ /// The actual results.
+ /// The method that failed an assertion.
+ /// The argument names and values of the method that failed an asserion.
+ /// The message to be displayed when assertion fails.
+ public static string GenerateAssertionMessage(IEnumerable source, IEnumerable> expected, IEnumerable> actual, string method, params (string argName, object? argValue)[] args)
+ {
+ static void AppendKeyValues(StringBuilder builder, string key, params (string subkey, object? value)[] keyValues)
+ {
+ static IEnumerable Enumerate(IEnumerable enumerable)
+ {
+ List list = [];
+
+ IEnumerator enumerator = enumerable.GetEnumerator();
+ while(enumerator.MoveNext())
+ {
+ list.Add(enumerator.Current);
+ }
+
+ return list;
+ }
+
+ static string ToString(object? obj)
+ {
+ return obj switch
+ {
+ null => "null",
+ IEnumerable enumerable => '[' + string.Join(", ", Enumerate(enumerable).Select(x => ToString(x))) + ']',
+ _ => obj.ToString()
+ } ?? "null";
+ }
+
+ builder.Append(key).AppendLine(":");
+ foreach((string subkey, object? value) in keyValues)
+ {
+ builder.Append('\t').Append(subkey).Append(": ").AppendLine(ToString(value));
+ }
+ }
+
+ static void AppendNestedEnumerable(StringBuilder builder, string key, IEnumerable> nestedEnumerable)
+ {
+ builder.Append(key).AppendLine(": [");
+ bool emptyCollection = true;
+ foreach(IEnumerable enumerable in nestedEnumerable)
+ {
+ emptyCollection = false;
+ builder.Append("\t[").AppendJoin(", ", enumerable).AppendLine("],");
+ }
+ builder.Length -= Environment.NewLine.Length + (!emptyCollection ? 1 : 0); // remove the last endline and comma
+ builder.AppendLine().AppendLine("]");
+ }
+
+ StringBuilder builder = new();
+
+ builder.AppendLine();
+ builder.Append("Runtime Version: ").AppendLine(Environment.Version.ToString());
+ builder.Append("Source: [").AppendJoin(", ", source).AppendLine("]");
+ builder.Append("Method: ").AppendLine(method);
+ AppendKeyValues(builder, "Arguments", args);
+ AppendNestedEnumerable(builder, "Expected", expected);
+ AppendNestedEnumerable(builder, "Actual", actual);
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Tests whether the specified nested collections are equal.
+ ///
+ /// The element type in the compared collections.
+ /// The first collection to compare.
+ /// The second collection to compare.
+ /// if both collections are equal; otherwise .
+ public static bool SequencesEqual(IEnumerable> first, IEnumerable> second) where T : IEquatable
+ {
+ IEnumerator> firstEnumerator = first.GetEnumerator();
+ IEnumerator> secondEnumerator = second.GetEnumerator();
+
+ while(firstEnumerator.MoveNext())
+ {
+ if(!secondEnumerator.MoveNext()) // first is longer
+ {
+ return false;
+ }
+
+ IEnumerator firstSubEnumerator = firstEnumerator.Current.GetEnumerator();
+ IEnumerator secondSubEnumerator = secondEnumerator.Current.GetEnumerator();
+
+ while(firstSubEnumerator.MoveNext())
+ {
+ if(!secondSubEnumerator.MoveNext()) // first is larger
+ {
+ return false;
+ }
+
+ if(!firstSubEnumerator.Current.Equals(secondSubEnumerator.Current))
+ {
+ return false;
+ }
+ }
+
+ if(secondSubEnumerator.MoveNext()) // second is larger
+ {
+ return false;
+ }
+ }
+
+ if(secondEnumerator.MoveNext()) // second is larger
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Tests whether calling the specified method on the specified source with the specified arguments results in the expected result.
+ ///
+ /// The element type of the source span.
+ /// The expected results.
+ /// The actual results.
+ /// The elements inside the source span.
+ /// The method that was called.
+ /// The argument names and values of the method that was called.
+ public static void AssertMethodResults(IEnumerable> expected, IEnumerable> actual, IEnumerable source, string method, params (string argName, object? argValue)[] args) where T : IEquatable
+ {
+ if(!SequencesEqual(expected, actual))
+ {
+ Assert.Fail(GenerateAssertionMessage(source, expected, actual, method, args));
+ }
+ }
+
+ ///
+ /// Creates a new span over the target array.
+ ///
+ /// The array type.
+ /// The target array.
+ /// A over .
+ public static ReadOnlySpan AsReadOnlySpan(this T[] source)
+ {
+ return source.AsSpan();
+ }
+
+#if !NET8_0_OR_GREATER
+ /// Counts the number of times the specified occurs in the .
+ /// The element type of the span.
+ /// The span to search.
+ /// The value for which to search.
+ /// The number of times was found in the .
+ public static int Count(this ReadOnlySpan span, T value) where T : IEquatable
+ {
+ int count = 0;
+ foreach(T item in span)
+ {
+ if(item.Equals(value))
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /// Counts the number of times the specified occurs in the .
+ /// The element type of the span.
+ /// The span to search.
+ /// The value for which to search.
+ /// The number of times was found in the .
+ public static int Count(this Span span, T value) where T : IEquatable
+ {
+ return Count((ReadOnlySpan)span, value);
+ }
+#endif
+
+ /// Counts the number of times the specified occurs in the .
+ /// The element type of the array.
+ /// The array to search.
+ /// The value for which to search.
+ /// The number of times was found in the .
+ public static int Count(this T[] array, T value) where T : IEquatable
+ {
+ return array.AsSpan().Count(value);
+ }
+
+ /// Counts the number of times the specified occurs in the .
+ /// The string to search.
+ /// The value for which to search.
+ /// The number of times was found in the .
+ public static int Count(this string @string, char value)
+ {
+ return @string.AsSpan().Count(value);
+ }
+
+#if !NET8_0_OR_GREATER
+ /// Counts the number of times any of the specified occur in the .
+ /// The element type of the span.
+ /// The span to search.
+ /// The values for which to search.
+ /// The number of times any of the was found in the .
+ public static int Count(this ReadOnlySpan span, ReadOnlySpan values) where T : IEquatable
+ {
+ int count = 0;
+ foreach(T item in span)
+ {
+ if(values.Contains(item))
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /// Counts the number of times any of the specified occur in the .
+ /// The element type of the span.
+ /// The span to search.
+ /// The values for which to search.
+ /// The number of times any of the was found in the .
+ public static int Count(this Span span, ReadOnlySpan values) where T : IEquatable
+ {
+ return Count((ReadOnlySpan)span, values);
+ }
+#endif
+
+ /// Counts the number of times any of the specified occur in the .
+ /// The element type of the array.
+ /// The array to search.
+ /// The values for which to search.
+ /// The number of times any of the was found in the .
+ public static int Count(this T[] array, ReadOnlySpan values) where T : IEquatable
+ {
+ return array.AsSpan().Count(values);
+ }
+
+ /// Counts the number of times any of the specified occur in the .
+ /// The string to search.
+ /// The values for which to search.
+ /// The number of times any of the was found in the .
+ public static int Count(this string @string, ReadOnlySpan values)
+ {
+ return @string.AsSpan().Count(values);
+ }
+
+ ///
+ /// Counts the (non-overlaping) occurrences of a subsequence in the target sequence.
+ ///
+ /// The element type in the sequence.
+ /// The target sequence.
+ /// The subsequence to count.
+ /// The number of occurrences of in .
+ public static int CountSubsequence(this ReadOnlySpan sequence, ReadOnlySpan subsequence) where T : IEquatable
+ {
+ if(sequence.IsEmpty || subsequence.IsEmpty)
+ {
+ return 0;
+ }
+
+ int count = 0, position;
+ while((position = sequence.IndexOf(subsequence)) != -1)
+ {
+ count++;
+ sequence = sequence[(position + subsequence.Length)..];
+ }
+ return count;
+ }
+
+ ///
+ /// Counts the (non-overlaping) occurrences of a subsequence in the target array.
+ ///
+ /// The element type in the array.
+ /// The target array.
+ /// The subsequence to count.
+ /// The number of occurrences of in .
+ public static int CountSubsequence(this T[] array, ReadOnlySpan subsequence) where T : IEquatable
+ {
+ return CountSubsequence(array.AsSpan(), subsequence);
+ }
+
+ ///
+ /// Counts the (non-overlaping) occurrences of a subsequence in the target string.
+ ///
+ /// The target string.
+ /// The subsequence to count.
+ /// The number of occurrences of in .
+ public static int CountSubsequence(this string @string, ReadOnlySpan subsequence)
+ {
+ return CountSubsequence(@string.AsSpan(), subsequence);
+ }
+
+ ///
+ /// Returns an indicating that the specified value wasn't handled.
+ ///
+ /// The value that wasn't handled.
+ public static Exception UnhandledCaseException(CountExceedingBehaviour countExceedingBehaviour)
+ {
+ return new
+#if NET7_0_OR_GREATER
+ UnreachableException
+#else
+ NotImplementedException
+#endif
+ ($"Unhandled {nameof(CountExceedingBehaviour)} enum value: {countExceedingBehaviour}.");
+ }
+
+ ///
+ /// Take up to a specified count of elements from an array.
+ /// Unlike [.. ], this doesn't throw an exception when is greater than the length of .
+ ///
+ /// The type of the array.
+ /// The array to cut.
+ /// The number of elements to take.
+ /// The cut array.
+ public static T[] UpTo(this T[] source, int count)
+ {
+ return source.Length <= count ? source : source[..count];
+ }
+
+ ///
+ /// Splits a sequence into a maximum number of subsequences based on a specified delimiter.
+ ///
+ /// The element type of the sequence.
+ /// The sequence to be split.
+ /// A that delimits the subsequences in .
+ /// The maximum number of splits. If zero, split on every occurence of .
+ /// Specifies how the elements after count splits should be handled.
+ /// A sequence of split subsequences.
+ /// If is negative.
+ public static IEnumerable> Split(IEnumerable source, T delimiter, int count = int.MaxValue, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable
+ {
+#if NET8_0_OR_GREATER
+ ArgumentOutOfRangeException.ThrowIfNegative(count);
+#else
+ if(count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+#endif
+
+ if(count != 0)
+ {
+ List segment = [];
+
+ foreach(T element in source)
+ {
+ if(count == 1 && countExceedingBehaviour == CountExceedingBehaviour.CutRemainingElements && element.Equals(delimiter))
+ {
+ break;
+ }
+
+ if(count == 1 || !element.Equals(delimiter))
+ {
+ segment.Add(element);
+ }
+ else
+ {
+ yield return segment;
+ segment = [];
+ count--;
+ }
+ }
+
+ yield return segment;
+ }
+ }
+
+ ///
+ /// Splits a sequence into a maximum number of subsequences based on specified delimiters.
+ ///
+ /// The element type of the sequence.
+ /// The sequence to be split.
+ /// A with the instances of that delimit the subsequences in .
+ /// The maximum number of splits. If zero, split on every occurence of .
+ /// Specifies how the elements after count splits should be handled.
+ /// A sequence of split subsequences.
+ /// If is negative.
+ public static IEnumerable> SplitAny(IEnumerable source, IEnumerable delimiters, int count = int.MaxValue, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable
+ {
+#if NET8_0_OR_GREATER
+ ArgumentOutOfRangeException.ThrowIfNegative(count);
+#else
+ if(count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+#endif
+
+ if(count != 0)
+ {
+
+ List segment = [];
+ foreach(T element in source)
+ {
+ if(count == 1 && countExceedingBehaviour == CountExceedingBehaviour.CutRemainingElements && delimiters.Any(delimiter => element.Equals(delimiter)))
+ {
+ break;
+ }
+
+ if(count == 1 || delimiters.All(delimiter => !element.Equals(delimiter)))
+ {
+ segment.Add(element);
+ }
+ else
+ {
+ yield return segment;
+ segment = [];
+ count--;
+ }
+ }
+
+ yield return segment;
+ }
+ }
+
+ ///
+ /// Splits a sequence into a maximum number of subsequences based on specified delimiter subsequence.
+ ///
+ /// The sequence to be split.
+ /// A that delimits the subsequences in .
+ /// The maximum number of splits. If zero, split on every occurence of .
+ /// Specifies how the elements after count splits should be handled.
+ /// A sequence of split subsequences.
+ /// If is negative.
+ public static IEnumerable> Split(IEnumerable source, IEnumerable delimiter, int count = int.MaxValue, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements) where T : IEquatable
+ {
+#if NET8_0_OR_GREATER
+ ArgumentOutOfRangeException.ThrowIfNegative(count);
+#else
+ if(count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+#endif
+
+ if(count == 0)
+ {
+ return [];
+ }
+
+ int delimiterLength = delimiter.Count();
+ if(delimiterLength == 0)
+ {
+ return [source];
+ }
+ else if(delimiterLength == 1)
+ {
+ return Split(source, delimiter.First(), count, countExceedingBehaviour);
+ }
+
+ string _source = '{' + string.Join("},{", source) + '}';
+ string _delimiter = '{' + string.Join("},{", delimiter) + '}';
+
+ string[] _splits = countExceedingBehaviour switch
+ {
+ CountExceedingBehaviour.AppendRemainingElements => _source.Split(_delimiter, count, StringSplitOptions.None),
+ CountExceedingBehaviour.CutRemainingElements => _source.Split(_delimiter, StringSplitOptions.None).UpTo(count),
+ _ => throw UnhandledCaseException(countExceedingBehaviour)
+ };
+
+ IEnumerable> splits = _splits.Select(s => s.Trim(',').Split(',').Where(x => x.Length != 0).Select(x => x[1..^1]).Where(x => x.Length != 0));
+
+ return source switch
+ {
+ IEnumerable => (IEnumerable>)splits.Select(s => s.Select(x => int.Parse(x, CultureInfo.InvariantCulture))),
+ IEnumerable => (IEnumerable>)splits.Select(s => s.Select(x => x[0])),
+ _ => throw new NotImplementedException($"Type {typeof(T)} was not implemented.")
+ };
+ }
+
+ ///
+ /// Splits a string into a maximum number of substrings based on a specified delimiting string and, optionally, options.
+ ///
+ /// The type of the separator. The only supported types: , , [] .
+ /// The string to be split.
+ /// A char/string/cahr[] that delimits the substrings in this instance.
+ /// The maximum number of elements expected in the array.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// Specifies how the elements after count splits should be handled.
+ /// An array that contains at most substrings from this instance that are delimited by .
+ ///
+ public static string[] Split(this string source, T separator, int count = int.MaxValue, StringSplitOptions options = StringSplitOptions.None, CountExceedingBehaviour countExceedingBehaviour = CountExceedingBehaviour.AppendRemainingElements)
+ {
+#if NET8_0_OR_GREATER
+ ArgumentOutOfRangeException.ThrowIfNegative(count);
+#else
+ if(count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+#endif
+
+ if(count == 0)
+ {
+ return [];
+ }
+
+ // When count is 1 and RemoveEmptyEntries option is set, it's a special case where splits shouldn't be recursively removed.
+ // Since string.Split doesn't have the CutRemainingElements option, we have to manually handle this.
+ static string[] FirstSubstring(string source, TSame separator, StringSplitOptions options = StringSplitOptions.None)
+ {
+ string first = separator switch
+ {
+ char charSeparator => source.Split(charSeparator)[0],
+ string stringSeparator => source.Split(stringSeparator)[0],
+ char[] charSeparators => source.Split(charSeparators)[0],
+ _ => throw new NotSupportedException($"Invalid separator type: {typeof(T)}")
+ };
+
+#if NET5_0_OR_GREATER
+ if(options.HasFlag(StringSplitOptions.TrimEntries))
+ {
+ first = first.Trim();
+ }
+#endif
+
+ if(options.HasFlag(StringSplitOptions.RemoveEmptyEntries) && string.IsNullOrEmpty(first))
+ {
+ return [];
+ }
+
+ return [first];
+ }
+
+ return separator switch
+ {
+ char charSeparator => countExceedingBehaviour switch
+ {
+ CountExceedingBehaviour.AppendRemainingElements => source.Split(charSeparator, count, options),
+ CountExceedingBehaviour.CutRemainingElements => count == 1 ? FirstSubstring(source, charSeparator, options) : source.Split(charSeparator, options).UpTo(count),
+ _ => throw UnhandledCaseException(countExceedingBehaviour)
+ },
+ string stringSeparator => countExceedingBehaviour switch
+ {
+ CountExceedingBehaviour.AppendRemainingElements => source.Split(stringSeparator, count, options),
+ CountExceedingBehaviour.CutRemainingElements => count == 1 ? FirstSubstring(source, stringSeparator, options) : source.Split(stringSeparator, options).UpTo(count),
+ _ => throw UnhandledCaseException(countExceedingBehaviour)
+ },
+ char[] charSeparators => countExceedingBehaviour switch
+ {
+ CountExceedingBehaviour.AppendRemainingElements => source.Split(charSeparators, count, options),
+ CountExceedingBehaviour.CutRemainingElements => count == 1 ? FirstSubstring(source, charSeparators, options) : source.Split(charSeparators, options).UpTo(count),
+ _ => throw UnhandledCaseException(countExceedingBehaviour)
+ },
+ _ => throw new NotSupportedException($"Invalid separator type: {typeof(T)}")
+ };
+ }
+
+ ///
+ /// Get an array with all permutations of the enum flags.
+ ///
+ /// All permutations of the enum flags.
+ public static StringSplitOptions[] GetAllStringSplitOptions()
+ {
+#if NET5_0_OR_GREATER
+ // ensure that no new option was added in an update
+ Debug.Assert(Enumerable.SequenceEqual(
+ (StringSplitOptions[])Enum.GetValues(typeof(StringSplitOptions)),
+ [StringSplitOptions.None, StringSplitOptions.RemoveEmptyEntries, StringSplitOptions.TrimEntries]
+ ));
+
+ return [
+ StringSplitOptions.None,
+ StringSplitOptions.RemoveEmptyEntries,
+ StringSplitOptions.TrimEntries,
+ StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
+ ];
+#else
+ return [
+ StringSplitOptions.None,
+ StringSplitOptions.RemoveEmptyEntries
+ ];
+#endif
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Linq/Sum.cs b/tests/fuzzing/Tests/ReadOnlySpan/Linq/Sum.cs
new file mode 100644
index 0000000..f4074fc
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Linq/Sum.cs
@@ -0,0 +1,12 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanLinqTests
+ {
+ public sealed class Sum
+ {
+ // todo
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Split/ReadOnlySpanSplitTests.cs b/tests/fuzzing/Tests/ReadOnlySpan/Split/ReadOnlySpanSplitTests.cs
new file mode 100644
index 0000000..5018d3e
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Split/ReadOnlySpanSplitTests.cs
@@ -0,0 +1,10 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanSplitTests
+ {
+ static readonly IEnumerable stringSplitOptions = GetAllStringSplitOptions();
+ static readonly CountExceedingBehaviour[] countExceedingBehaviours = (CountExceedingBehaviour[])Enum.GetValues(typeof(CountExceedingBehaviour));
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Split/Split.cs b/tests/fuzzing/Tests/ReadOnlySpan/Split/Split.cs
new file mode 100644
index 0000000..acc4f3b
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Split/Split.cs
@@ -0,0 +1,115 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanSplitTests
+ {
+ public static class Split
+ {
+ public static TheoryData SplitData(int iterations)
+ {
+ const int minValue = 0;
+ const int maxValue = 100;
+
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ data.Add(iterations, length, minValue, maxValue);
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithoutParameters
+ {
+ public static readonly TheoryData _SplitData = SplitData(250000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue)
+ {
+ static void AssertOptions(T[] array, T delimiter) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter),
+ actual: array.AsReadOnlySpan().Split(delimiter).ToSystemEnumerable(),
+ source: array,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: ("delimiter", delimiter)
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int integerDelimiter = integerArray.RandomElementOrDefault(0);
+ AssertOptions(integerArray, integerDelimiter);
+ AssertOptions(integerArray, maxValue);
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char charDelimiter = charArray.RandomElementOrDefault('0');
+ const char charMissingDelimiter = 'ა';
+ AssertOptions(charArray, charDelimiter);
+ AssertOptions(charArray, charMissingDelimiter);
+ }
+ }
+ }
+
+ public sealed class SplitWithCount
+ {
+ public static readonly TheoryData _SplitData = SplitData(25000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue)
+ {
+ static void AssertOptions(T[] array, T delimiter, int count, CountExceedingBehaviour countExceedingBehaviour) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter, count, countExceedingBehaviour),
+ actual: array.AsReadOnlySpan().Split(delimiter, count, countExceedingBehaviour).ToSystemEnumerable(),
+ source: array,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int integerDelimiter = integerArray.RandomElementOrDefault(0);
+ int countDelimiters = integerArray.Count(integerDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(integerArray, integerDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(integerArray, integerDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(integerArray, integerDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(integerArray, maxValue, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, maxValue, 1, countExceedingBehaviour);
+ AssertOptions(integerArray, maxValue, 2, countExceedingBehaviour);
+ }
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char charDelimiter = charArray.RandomElementOrDefault('0');
+ const char charMissingDelimiter = 'ა';
+ countDelimiters = charArray.Count(charDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(charArray, charDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(charArray, charDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(charArray, charDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(charArray, charMissingDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charMissingDelimiter, 1, countExceedingBehaviour);
+ AssertOptions(charArray, charMissingDelimiter, 2, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitAny.cs b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitAny.cs
new file mode 100644
index 0000000..ac845af
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitAny.cs
@@ -0,0 +1,133 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanSplitTests
+ {
+ public static class SplitAny
+ {
+ public static TheoryData SplitAnyData(int iterations)
+ {
+ const int minValue = 0;
+ const int maxValue = 100;
+
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimitersLength in ((IEnumerable)[0, 1, 5, 25, 50]).Where(x => x <= length * 3))
+ {
+ foreach(float delimitersOccurencePart in GetParts(delimitersLength))
+ {
+ data.Add(iterations, length, minValue, maxValue, delimitersLength, delimitersOccurencePart);
+ }
+ }
+ }
+
+ return data;
+
+ static IEnumerable GetParts(int delimitersLength)
+ {
+ return delimitersLength switch
+ {
+ 0 => [0f],
+ 1 => [0f, 1f],
+ _ => [0f, 0.5f, 1f]
+ };
+ }
+ }
+
+ public sealed class SplitAnyWithoutParameters
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(11000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(T[] array, T[] delimiters) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: SplitAny(array, delimiters),
+ actual: array.AsReadOnlySpan().SplitAny(delimiters).ToSystemEnumerable(),
+ source: array,
+ method: nameof(ReadOnlySpanExtensions.SplitAny),
+ args: ("delimiters", delimiters)
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? integerArray.RandomElementOrDefault()
+ : maxValue + i
+ ).ToArray();
+ AssertOptions(integerArray, integerDelimiters);
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? charArray.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ AssertOptions(charArray, charDelimiters);
+ }
+ }
+ }
+
+ public sealed class SplitAnyWithCount
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(2000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(T[] array, T[] delimiters, int count, CountExceedingBehaviour countExceedingBehaviour) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: SplitAny(array, delimiters, count, countExceedingBehaviour),
+ actual: array.AsReadOnlySpan().SplitAny(delimiters, count, countExceedingBehaviour).ToSystemEnumerable(),
+ source: array,
+ method: nameof(ReadOnlySpanExtensions.SplitAny),
+ args: [("delimiters", delimiters), ("count", count), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? integerArray.RandomElementOrDefault()
+ : maxValue + i
+ ).ToArray();
+ int countDelimiters = integerArray.Count(integerDelimiters);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(integerArray, integerDelimiters, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiters, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(integerArray, integerDelimiters, countDelimiters, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(integerArray, integerDelimiters, countDelimiters, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiters, countDelimiters + 2, countExceedingBehaviour);
+ }
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? charArray.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ countDelimiters = charArray.Count(charDelimiters);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(charArray, charDelimiters, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiters, 0, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(charArray, charDelimiters, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(charArray, charDelimiters, countDelimiters, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiters, countDelimiters + 2, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitAnyString.cs b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitAnyString.cs
new file mode 100644
index 0000000..8f77ae7
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitAnyString.cs
@@ -0,0 +1,114 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanSplitTests
+ {
+ public static class SplitAnyString
+ {
+ public static TheoryData SplitAnyData(int iterations)
+ {
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimitersLength in ((IEnumerable)[0, 1, 5, 25, 50]).Where(x => x <= length * 3))
+ {
+ foreach(float delimitersOccurencePart in GetParts(delimitersLength))
+ {
+ data.Add(iterations, length, delimitersLength, delimitersOccurencePart);
+ }
+ }
+ }
+
+ return data;
+
+ static IEnumerable GetParts(int delimitersLength)
+ {
+ return delimitersLength switch
+ {
+ 0 => [0f],
+ 1 => [0f, 1f],
+ _ => [0f, 0.5f, 1f]
+ };
+ }
+ }
+
+ public sealed class SplitAnyWithoutParameters
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(15000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(string @string, char[] delimiters, StringSplitOptions options)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiters, options),
+ actual: @string.AsSpan().SplitAny(delimiters, options).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(ReadOnlySpanExtensions.SplitAny),
+ args: [("delimiters", delimiters), ("options", options)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? @string.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ AssertOptions(@string, charDelimiters, options);
+ }
+ }
+ }
+ }
+
+ public sealed class SplitAnyWithCount
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(2000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(string @string, char[] delimiters, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiters, count, options, countExceedingBehaviour),
+ actual: @string.AsSpan().SplitAny(delimiters, count, options, countExceedingBehaviour).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(ReadOnlySpanExtensions.SplitAny),
+ args: [("delimiters", delimiters), ("count", count), ("options", options), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? @string.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ int countDelimiters = @string.AsSpan().Count(charDelimiters);
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(@string, charDelimiters, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiters, 1, options, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(@string, charDelimiters, countDelimiters - 1, options, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(@string, charDelimiters, countDelimiters, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiters, countDelimiters + 2, options, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitSequence.cs b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitSequence.cs
new file mode 100644
index 0000000..1bd9338
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitSequence.cs
@@ -0,0 +1,124 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanSplitTests
+ {
+ public static class SplitSequence
+ {
+ public static TheoryData SplitWithDelimiterSequenceData(int iterations)
+ {
+ const int minValue = 0;
+ const int maxValue = 100;
+
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimiterLength in new MultiplierRange(3, length * 10, 10).And([0, 1]))
+ {
+ data.Add(iterations, length, minValue, maxValue, delimiterLength);
+ }
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithDelimiterSequence
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(20000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimiterLength)
+ {
+ static void AssertOptions(T[] array, T[] delimiter) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter),
+ actual: array.AsReadOnlySpan().Split(delimiter).ToSystemEnumerable(),
+ source: array,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: ("delimiter", delimiter)
+ );
+ }
+
+ int[] randomIntDelimiterArray = GenerateRandomIntegers(delimiterLength, minValue, maxValue).ToArray();
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerSequenceDelimiter = integerArray.RandomSubsequenceOrDefault(delimiterLength, randomIntDelimiterArray);
+ int[] integerSequenceMissingDelimiter = integerSequenceDelimiter.ReplaceRandomElement(maxValue);
+ AssertOptions(integerArray, integerSequenceDelimiter);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter);
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charSequenceDelimiter = charArray.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ AssertOptions(charArray, charSequenceDelimiter);
+ AssertOptions(charArray, charSequenceMissingDelimiter);
+ }
+ }
+ }
+
+ public sealed class SplitWithDelimiterSequenceAndCount
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(5000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimiterLength)
+ {
+ static void AssertOptions(T[] array, T[] delimiter, int count, CountExceedingBehaviour countExceedingBehaviour) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter, count, countExceedingBehaviour),
+ actual: array.AsReadOnlySpan().Split(delimiter, count, countExceedingBehaviour).ToSystemEnumerable(),
+ source: array,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ int[] randomIntDelimiterArray = GenerateRandomIntegers(delimiterLength, minValue, maxValue).ToArray();
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerSequenceDelimiter = integerArray.RandomSubsequenceOrDefault(delimiterLength, randomIntDelimiterArray);
+ int[] integerSequenceMissingDelimiter = integerSequenceDelimiter.ReplaceRandomElement(maxValue);
+ int countDelimiters = integerArray.CountSubsequence(integerSequenceDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(integerArray, integerSequenceDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(integerArray, integerSequenceDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(integerArray, integerSequenceDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter, 1, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter, 2, countExceedingBehaviour);
+ }
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charSequenceDelimiter = charArray.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ countDelimiters = charArray.CountSubsequence(charSequenceDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(charArray, charSequenceDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(charArray, charSequenceDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(charArray, charSequenceDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceMissingDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceMissingDelimiter, 1, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceMissingDelimiter, 2, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitString.cs b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitString.cs
new file mode 100644
index 0000000..32b1830
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitString.cs
@@ -0,0 +1,97 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanSplitTests
+ {
+ public static class SplitString
+ {
+ public static TheoryData SplitData(int iterations)
+ {
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ data.Add(iterations, length);
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithoutParameters
+ {
+ public static readonly TheoryData _SplitData = SplitData(125000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length)
+ {
+ static void AssertOptions(string @string, char delimiter, StringSplitOptions options)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiter, options),
+ actual: @string.AsSpan().Split(delimiter, options).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: [("delimiter", delimiter), ("options", options)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char charDelimiter = @string.RandomElementOrDefault('0');
+ const char charMissingDelimiter = 'ა';
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ AssertOptions(@string, charDelimiter, options);
+ AssertOptions(@string, charMissingDelimiter, options);
+ }
+ }
+ }
+ }
+
+ public sealed class SplitWithCount
+ {
+ public static readonly TheoryData _SplitData = SplitData(20000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length)
+ {
+ static void AssertOptions(string @string, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiter, count, options, countExceedingBehaviour),
+ actual: @string.AsSpan().Split(delimiter, count, options, countExceedingBehaviour).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("options", options), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char charDelimiter = @string.RandomElementOrDefault('0');
+ int countDelimiters = @string.Count(charDelimiter);
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(@string, charDelimiter, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiter, 1, options, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(@string, charDelimiter, countDelimiters - 1, options, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(@string, charDelimiter, countDelimiters, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiter, countDelimiters + 2, options, countExceedingBehaviour);
+ AssertOptions(@string, 'ა', 0, options, countExceedingBehaviour);
+ AssertOptions(@string, 'ა', 1, options, countExceedingBehaviour);
+ AssertOptions(@string, 'ა', 2, options, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitStringSequence.cs b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitStringSequence.cs
new file mode 100644
index 0000000..792a1ee
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/Split/SplitStringSequence.cs
@@ -0,0 +1,103 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanSplitTests
+ {
+ public static class SplitStringSequence
+ {
+ public static TheoryData SplitWithDelimiterSequenceData(int iterations)
+ {
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimiterLength in new MultiplierRange(3, length * 10, 10).And([0, 1]))
+ {
+ data.Add(iterations, length, delimiterLength);
+ }
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithDelimiterSequence
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(30000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int delimiterLength)
+ {
+ static void AssertOptions(string @string, char[] delimiter, StringSplitOptions options)
+ {
+ AssertMethodResults(
+ expected: @string.Split(new string(delimiter), options),
+ actual: @string.AsSpan().Split(delimiter, options).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: [("delimiter", delimiter), ("options", options)]
+ );
+ }
+
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charSequenceDelimiter = @string.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ AssertOptions(@string, charSequenceDelimiter, options);
+ AssertOptions(@string, charSequenceMissingDelimiter, options);
+ }
+ }
+ }
+ }
+
+ public sealed class SplitWithDelimiterSequenceAndCount
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(10000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int delimiterLength)
+ {
+ static void AssertOptions(string @string, char[] delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour)
+ {
+ AssertMethodResults(
+ expected: @string.Split(new string(delimiter), count, options, countExceedingBehaviour),
+ actual: @string.AsSpan().Split(delimiter, count, options, countExceedingBehaviour).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(ReadOnlySpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("options", options), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charSequenceDelimiter = @string.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ int countDelimiters = @string.CountSubsequence(charSequenceDelimiter);
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(@string, charSequenceDelimiter, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceDelimiter, 1, options, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(@string, charSequenceDelimiter, countDelimiters - 1, options, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(@string, charSequenceDelimiter, countDelimiters, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceDelimiter, countDelimiters + 2, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceMissingDelimiter, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceMissingDelimiter, 1, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceMissingDelimiter, 2, options, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/ReadOnlySpan/String/Remove.cs b/tests/fuzzing/Tests/ReadOnlySpan/String/Remove.cs
new file mode 100644
index 0000000..457d549
--- /dev/null
+++ b/tests/fuzzing/Tests/ReadOnlySpan/String/Remove.cs
@@ -0,0 +1,12 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class ReadOnlySpanStringTests
+ {
+ public sealed class Remove
+ {
+ // todo
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Linq/Sum.cs b/tests/fuzzing/Tests/Span/Linq/Sum.cs
new file mode 100644
index 0000000..4c78d6d
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Linq/Sum.cs
@@ -0,0 +1,12 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanLinqTests
+ {
+ public sealed class Sum
+ {
+ // todo
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Split/SpanSplitTests.cs b/tests/fuzzing/Tests/Span/Split/SpanSplitTests.cs
new file mode 100644
index 0000000..7b87d94
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Split/SpanSplitTests.cs
@@ -0,0 +1,10 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanSplitTests
+ {
+ static readonly IEnumerable stringSplitOptions = GetAllStringSplitOptions();
+ static readonly CountExceedingBehaviour[] countExceedingBehaviours = (CountExceedingBehaviour[])Enum.GetValues(typeof(CountExceedingBehaviour));
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Split/Split.cs b/tests/fuzzing/Tests/Span/Split/Split.cs
new file mode 100644
index 0000000..05af533
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Split/Split.cs
@@ -0,0 +1,115 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanSplitTests
+ {
+ public static class Split
+ {
+ public static TheoryData SplitData(int iterations)
+ {
+ const int minValue = 0;
+ const int maxValue = 100;
+
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ data.Add(iterations, length, minValue, maxValue);
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithoutParameters
+ {
+ public static readonly TheoryData _SplitData = SplitData(250000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue)
+ {
+ static void AssertOptions(T[] array, T delimiter) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter),
+ actual: array.AsSpan().Split(delimiter).ToSystemEnumerable(),
+ source: array,
+ method: nameof(SpanExtensions.Split),
+ args: ("delimiter", delimiter)
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int integerDelimiter = integerArray.RandomElementOrDefault(0);
+ AssertOptions(integerArray, integerDelimiter);
+ AssertOptions(integerArray, maxValue);
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char charDelimiter = charArray.RandomElementOrDefault('0');
+ const char charMissingDelimiter = 'ა';
+ AssertOptions(charArray, charDelimiter);
+ AssertOptions(charArray, charMissingDelimiter);
+ }
+ }
+ }
+
+ public sealed class SplitWithCount
+ {
+ public static readonly TheoryData _SplitData = SplitData(25000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue)
+ {
+ static void AssertOptions(T[] array, T delimiter, int count, CountExceedingBehaviour countExceedingBehaviour) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter, count, countExceedingBehaviour),
+ actual: array.AsSpan().Split(delimiter, count, countExceedingBehaviour).ToSystemEnumerable(),
+ source: array,
+ method: nameof(SpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int integerDelimiter = integerArray.RandomElementOrDefault(0);
+ int countDelimiters = integerArray.Count(integerDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(integerArray, integerDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(integerArray, integerDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(integerArray, integerDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(integerArray, maxValue, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, maxValue, 1, countExceedingBehaviour);
+ AssertOptions(integerArray, maxValue, 2, countExceedingBehaviour);
+ }
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char charDelimiter = charArray.RandomElementOrDefault('0');
+ const char charMissingDelimiter = 'ა';
+ countDelimiters = charArray.Count(charDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(charArray, charDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(charArray, charDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(charArray, charDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(charArray, charMissingDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charMissingDelimiter, 1, countExceedingBehaviour);
+ AssertOptions(charArray, charMissingDelimiter, 2, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Split/SplitAny.cs b/tests/fuzzing/Tests/Span/Split/SplitAny.cs
new file mode 100644
index 0000000..8dde1d0
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Split/SplitAny.cs
@@ -0,0 +1,133 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanSplitTests
+ {
+ public static class SplitAny
+ {
+ public static TheoryData SplitAnyData(int iterations)
+ {
+ const int minValue = 0;
+ const int maxValue = 100;
+
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimitersLength in ((IEnumerable)[0, 1, 5, 25, 50]).Where(x => x <= length * 3))
+ {
+ foreach(float delimitersOccurencePart in GetParts(delimitersLength))
+ {
+ data.Add(iterations, length, minValue, maxValue, delimitersLength, delimitersOccurencePart);
+ }
+ }
+ }
+
+ return data;
+
+ static IEnumerable GetParts(int delimitersLength)
+ {
+ return delimitersLength switch
+ {
+ 0 => [0f],
+ 1 => [0f, 1f],
+ _ => [0f, 0.5f, 1f]
+ };
+ }
+ }
+
+ public sealed class SplitAnyWithoutParameters
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(11000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(T[] array, T[] delimiters) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: SplitAny(array, delimiters),
+ actual: array.AsSpan().SplitAny(delimiters).ToSystemEnumerable(),
+ source: array,
+ method: nameof(SpanExtensions.SplitAny),
+ args: ("delimiters", delimiters)
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? integerArray.RandomElementOrDefault()
+ : maxValue + i
+ ).ToArray();
+ AssertOptions(integerArray, integerDelimiters);
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? charArray.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ AssertOptions(charArray, charDelimiters);
+ }
+ }
+ }
+
+ public sealed class SplitAnyWithCount
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(2000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(T[] array, T[] delimiters, int count, CountExceedingBehaviour countExceedingBehaviour) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: SplitAny(array, delimiters, count, countExceedingBehaviour),
+ actual: array.AsSpan().SplitAny(delimiters, count, countExceedingBehaviour).ToSystemEnumerable(),
+ source: array,
+ method: nameof(SpanExtensions.SplitAny),
+ args: [("delimiters", delimiters), ("count", count), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? integerArray.RandomElementOrDefault()
+ : maxValue + i
+ ).ToArray();
+ int countDelimiters = integerArray.Count(integerDelimiters);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(integerArray, integerDelimiters, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiters, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(integerArray, integerDelimiters, countDelimiters, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(integerArray, integerDelimiters, countDelimiters, countExceedingBehaviour);
+ AssertOptions(integerArray, integerDelimiters, countDelimiters + 2, countExceedingBehaviour);
+ }
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? charArray.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ countDelimiters = charArray.Count(charDelimiters);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(charArray, charDelimiters, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiters, 0, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(charArray, charDelimiters, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(charArray, charDelimiters, countDelimiters, countExceedingBehaviour);
+ AssertOptions(charArray, charDelimiters, countDelimiters + 2, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Split/SplitAnyString.cs b/tests/fuzzing/Tests/Span/Split/SplitAnyString.cs
new file mode 100644
index 0000000..4b37ea1
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Split/SplitAnyString.cs
@@ -0,0 +1,114 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanSplitTests
+ {
+ public static class SplitAnyString
+ {
+ public static TheoryData SplitAnyData(int iterations)
+ {
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimitersLength in ((IEnumerable)[0, 1, 5, 25, 50]).Where(x => x <= length * 3))
+ {
+ foreach(float delimitersOccurencePart in GetParts(delimitersLength))
+ {
+ data.Add(iterations, length, delimitersLength, delimitersOccurencePart);
+ }
+ }
+ }
+
+ return data;
+
+ static IEnumerable GetParts(int delimitersLength)
+ {
+ return delimitersLength switch
+ {
+ 0 => [0f],
+ 1 => [0f, 1f],
+ _ => [0f, 0.5f, 1f]
+ };
+ }
+ }
+
+ public sealed class SplitAnyWithoutParameters
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(15000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(string @string, char[] delimiters, StringSplitOptions options)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiters, options),
+ actual: @string.ToCharArray().AsSpan().SplitAny(delimiters, options).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(SpanExtensions.SplitAny),
+ args: [("delimiters", delimiters), ("options", options)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? @string.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ AssertOptions(@string, charDelimiters, options);
+ }
+ }
+ }
+ }
+
+ public sealed class SplitAnyWithCount
+ {
+ public static readonly TheoryData _SplitAnyData = SplitAnyData(2000);
+
+ [Theory]
+ [MemberData(nameof(_SplitAnyData))]
+ public void Fuzz(int iterations, int length, int delimitersLength, float delimitersOccurencePart)
+ {
+ static void AssertOptions(string @string, char[] delimiters, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiters, count, options, countExceedingBehaviour),
+ actual: @string.ToCharArray().AsSpan().SplitAny(delimiters, count, options, countExceedingBehaviour).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(SpanExtensions.SplitAny),
+ args: [("delimiters", delimiters), ("count", count), ("options", options), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charDelimiters = Enumerable.Range(0, delimitersLength).Select(i =>
+ i < delimitersLength * delimitersOccurencePart ? @string.RandomElementOrDefault()
+ : (char)('ა' + i)
+ ).ToArray();
+ int countDelimiters = @string.AsSpan().Count(charDelimiters);
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(@string, charDelimiters, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiters, 1, options, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(@string, charDelimiters, countDelimiters - 1, options, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(@string, charDelimiters, countDelimiters, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiters, countDelimiters + 2, options, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Split/SplitSequence.cs b/tests/fuzzing/Tests/Span/Split/SplitSequence.cs
new file mode 100644
index 0000000..6cc3291
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Split/SplitSequence.cs
@@ -0,0 +1,124 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanSplitTests
+ {
+ public static class SplitSequence
+ {
+ public static TheoryData SplitWithDelimiterSequenceData(int iterations)
+ {
+ const int minValue = 0;
+ const int maxValue = 100;
+
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimiterLength in new MultiplierRange(3, length * 10, 10).And([0, 1]))
+ {
+ data.Add(iterations, length, minValue, maxValue, delimiterLength);
+ }
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithDelimiterSequence
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(20000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimiterLength)
+ {
+ static void AssertOptions(T[] array, T[] delimiter) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter),
+ actual: array.AsSpan().Split(delimiter).ToSystemEnumerable(),
+ source: array,
+ method: nameof(SpanExtensions.Split),
+ args: ("delimiter", delimiter)
+ );
+ }
+
+ int[] randomIntDelimiterArray = GenerateRandomIntegers(delimiterLength, minValue, maxValue).ToArray();
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerSequenceDelimiter = integerArray.RandomSubsequenceOrDefault(delimiterLength, randomIntDelimiterArray);
+ int[] integerSequenceMissingDelimiter = integerSequenceDelimiter.ReplaceRandomElement(maxValue);
+ AssertOptions(integerArray, integerSequenceDelimiter);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter);
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charSequenceDelimiter = charArray.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ AssertOptions(charArray, charSequenceDelimiter);
+ AssertOptions(charArray, charSequenceMissingDelimiter);
+ }
+ }
+ }
+
+ public sealed class SplitWithDelimiterSequenceAndCount
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(5000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int minValue, int maxValue, int delimiterLength)
+ {
+ static void AssertOptions(T[] array, T[] delimiter, int count, CountExceedingBehaviour countExceedingBehaviour) where T : IEquatable
+ {
+ AssertMethodResults(
+ expected: Split(array, delimiter, count, countExceedingBehaviour),
+ actual: array.AsSpan().Split(delimiter, count, countExceedingBehaviour).ToSystemEnumerable(),
+ source: array,
+ method: nameof(SpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ int[] randomIntDelimiterArray = GenerateRandomIntegers(delimiterLength, minValue, maxValue).ToArray();
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ int[] integerArray = GenerateRandomIntegers(length, minValue, maxValue).ToArray();
+ int[] integerSequenceDelimiter = integerArray.RandomSubsequenceOrDefault(delimiterLength, randomIntDelimiterArray);
+ int[] integerSequenceMissingDelimiter = integerSequenceDelimiter.ReplaceRandomElement(maxValue);
+ int countDelimiters = integerArray.CountSubsequence(integerSequenceDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(integerArray, integerSequenceDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(integerArray, integerSequenceDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(integerArray, integerSequenceDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter, 1, countExceedingBehaviour);
+ AssertOptions(integerArray, integerSequenceMissingDelimiter, 2, countExceedingBehaviour);
+ }
+
+ char[] charArray = GenerateRandomString(length).ToCharArray();
+ char[] charSequenceDelimiter = charArray.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ countDelimiters = charArray.CountSubsequence(charSequenceDelimiter);
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(charArray, charSequenceDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceDelimiter, 1, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(charArray, charSequenceDelimiter, countDelimiters - 1, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(charArray, charSequenceDelimiter, countDelimiters, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceDelimiter, countDelimiters + 2, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceMissingDelimiter, 0, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceMissingDelimiter, 1, countExceedingBehaviour);
+ AssertOptions(charArray, charSequenceMissingDelimiter, 2, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Split/SplitString.cs b/tests/fuzzing/Tests/Span/Split/SplitString.cs
new file mode 100644
index 0000000..849275e
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Split/SplitString.cs
@@ -0,0 +1,97 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanSplitTests
+ {
+ public static class SplitString
+ {
+ public static TheoryData SplitData(int iterations)
+ {
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ data.Add(iterations, length);
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithoutParameters
+ {
+ public static readonly TheoryData _SplitData = SplitData(125000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length)
+ {
+ static void AssertOptions(string @string, char delimiter, StringSplitOptions options)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiter, options),
+ actual: @string.ToCharArray().AsSpan().Split(delimiter, options).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(SpanExtensions.Split),
+ args: [("delimiter", delimiter), ("options", options)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char charDelimiter = @string.RandomElementOrDefault('0');
+ const char charMissingDelimiter = 'ა';
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ AssertOptions(@string, charDelimiter, options);
+ AssertOptions(@string, charMissingDelimiter, options);
+ }
+ }
+ }
+ }
+
+ public sealed class SplitWithCount
+ {
+ public static readonly TheoryData _SplitData = SplitData(20000);
+
+ [Theory]
+ [MemberData(nameof(_SplitData))]
+ public void Fuzz(int iterations, int length)
+ {
+ static void AssertOptions(string @string, char delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour)
+ {
+ AssertMethodResults(
+ expected: @string.Split(delimiter, count, options, countExceedingBehaviour),
+ actual: @string.ToCharArray().AsSpan().Split(delimiter, count, options, countExceedingBehaviour).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(SpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("options", options), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char charDelimiter = @string.RandomElementOrDefault('0');
+ int countDelimiters = @string.Count(charDelimiter);
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(@string, charDelimiter, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiter, 1, options, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(@string, charDelimiter, countDelimiters - 1, options, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(@string, charDelimiter, countDelimiters, options, countExceedingBehaviour);
+ AssertOptions(@string, charDelimiter, countDelimiters + 2, options, countExceedingBehaviour);
+ AssertOptions(@string, 'ა', 0, options, countExceedingBehaviour);
+ AssertOptions(@string, 'ა', 1, options, countExceedingBehaviour);
+ AssertOptions(@string, 'ა', 2, options, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/Split/SplitStringSequence.cs b/tests/fuzzing/Tests/Span/Split/SplitStringSequence.cs
new file mode 100644
index 0000000..3764e6c
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/Split/SplitStringSequence.cs
@@ -0,0 +1,103 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanSplitTests
+ {
+ public static class SplitStringSequence
+ {
+ public static TheoryData SplitWithDelimiterSequenceData(int iterations)
+ {
+ TheoryData data = new();
+
+ foreach(int length in new MultiplierRange(1, 1000, 10).And([0]))
+ {
+ foreach(int delimiterLength in new MultiplierRange(3, length * 10, 10).And([0, 1]))
+ {
+ data.Add(iterations, length, delimiterLength);
+ }
+ }
+
+ return data;
+ }
+
+ public sealed class SplitWithDelimiterSequence
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(30000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int delimiterLength)
+ {
+ static void AssertOptions(string @string, char[] delimiter, StringSplitOptions options)
+ {
+ AssertMethodResults(
+ expected: @string.Split(new string(delimiter), options),
+ actual: @string.ToCharArray().AsSpan().Split(delimiter, options).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(SpanExtensions.Split),
+ args: [("delimiter", delimiter), ("options", options)]
+ );
+ }
+
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charSequenceDelimiter = @string.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ AssertOptions(@string, charSequenceDelimiter, options);
+ AssertOptions(@string, charSequenceMissingDelimiter, options);
+ }
+ }
+ }
+ }
+
+ public sealed class SplitWithDelimiterSequenceAndCount
+ {
+ public static readonly TheoryData _SplitWithDelimiterSequenceData = SplitWithDelimiterSequenceData(10000);
+
+ [Theory]
+ [MemberData(nameof(_SplitWithDelimiterSequenceData))]
+ public void Fuzz(int iterations, int length, int delimiterLength)
+ {
+ static void AssertOptions(string @string, char[] delimiter, int count, StringSplitOptions options, CountExceedingBehaviour countExceedingBehaviour)
+ {
+ AssertMethodResults(
+ expected: @string.Split(new string(delimiter), count, options, countExceedingBehaviour),
+ actual: @string.ToCharArray().AsSpan().Split(delimiter, count, options, countExceedingBehaviour).ToSystemEnumerable(),
+ source: @string,
+ method: nameof(SpanExtensions.Split),
+ args: [("delimiter", delimiter), ("count", count), ("options", options), ("countExceedingBehaviour", countExceedingBehaviour)]
+ );
+ }
+
+ char[] randomcharDelimiterArray = GenerateRandomString(delimiterLength).ToCharArray();
+ for(int iteration = 0; iteration < iterations; iteration++)
+ {
+ string @string = GenerateRandomString(length);
+ char[] charSequenceDelimiter = @string.RandomSubsequenceOrDefault(delimiterLength, randomcharDelimiterArray);
+ char[] charSequenceMissingDelimiter = charSequenceDelimiter.ReplaceRandomElement('ა');
+ int countDelimiters = @string.CountSubsequence(charSequenceDelimiter);
+ foreach(StringSplitOptions options in stringSplitOptions)
+ {
+ foreach(CountExceedingBehaviour countExceedingBehaviour in countExceedingBehaviours)
+ {
+ AssertOptions(@string, charSequenceDelimiter, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceDelimiter, 1, options, countExceedingBehaviour);
+ if(countDelimiters - 1 > 1) AssertOptions(@string, charSequenceDelimiter, countDelimiters - 1, options, countExceedingBehaviour);
+ if(countDelimiters > 1) AssertOptions(@string, charSequenceDelimiter, countDelimiters, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceDelimiter, countDelimiters + 2, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceMissingDelimiter, 0, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceMissingDelimiter, 1, options, countExceedingBehaviour);
+ AssertOptions(@string, charSequenceMissingDelimiter, 2, options, countExceedingBehaviour);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/fuzzing/Tests/Span/String/Remove.cs b/tests/fuzzing/Tests/Span/String/Remove.cs
new file mode 100644
index 0000000..0403c06
--- /dev/null
+++ b/tests/fuzzing/Tests/Span/String/Remove.cs
@@ -0,0 +1,12 @@
+using static SpanExtensions.Tests.Fuzzing.TestHelper;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ public static partial class SpanStringTests
+ {
+ public sealed class Remove
+ {
+ // todo
+ }
+ }
+}
diff --git a/tests/fuzzing/ToSystemEnumerableExtensions.cs b/tests/fuzzing/ToSystemEnumerableExtensions.cs
new file mode 100644
index 0000000..ba3ab80
--- /dev/null
+++ b/tests/fuzzing/ToSystemEnumerableExtensions.cs
@@ -0,0 +1,155 @@
+using SpanExtensions.Enumerators;
+
+namespace SpanExtensions.Tests.Fuzzing
+{
+ ///
+ /// Extension methods to convert enumerators into .
+ /// This obviously defeats the purpose of using spans. This is for testing only .
+ ///
+ public static class ToSystemEnumerableExtensions
+ {
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitEnumerator spanEnumerator) where T : IEquatable
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitWithCountEnumerator spanEnumerator) where T : IEquatable
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitStringSplitOptionsEnumerator spanEnumerator)
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitStringSplitOptionsWithCountEnumerator spanEnumerator)
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitAnyEnumerator spanEnumerator) where T : IEquatable
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitAnyWithCountEnumerator spanEnumerator) where T : IEquatable
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitAnyStringSplitOptionsEnumerator spanEnumerator)
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitAnyStringSplitOptionsWithCountEnumerator spanEnumerator)
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitSequenceEnumerator spanEnumerator) where T : IEquatable
+ {
+ List list = [];
+
+ foreach(ReadOnlySpan element in spanEnumerator)
+ {
+ list.Add(element.ToArray());
+ }
+
+ return list;
+ }
+
+ public static IEnumerable> ToSystemEnumerable(this SpanSplitSequenceWithCountEnumerator spanEnumerator) where T : IEquatable
+ {
+ List