Skip to content
Merged
52 changes: 52 additions & 0 deletions src/libraries/System.Memory/tests/Span/IndexOfAnyInRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,58 @@ public void MatrixOfLengthsAndOffsets_AnyExcept_FindsValueAtExpectedIndex()
}
}

[Fact]
Comment thread
stephentoub marked this conversation as resolved.
Outdated
public void InvalidRange_HighLessThanLow_ReturnsMinus1()
{
// When highInclusive < lowInclusive, the range is invalid and no values should match
for (int length = 1; length <= 64; length++)
{
T[] array = Enumerable.Range(0, length).Select(Create).ToArray();

// Test with a clearly invalid range where high < low
T lowInclusive = Create(100);
T highInclusive = Create(50);

// IndexOfAnyInRange should return -1 (no match)
Assert.Equal(-1, IndexOfAnyInRange(array, lowInclusive, highInclusive));

// LastIndexOfAnyInRange should return -1 (no match)
Assert.Equal(-1, LastIndexOfAnyInRange(array, lowInclusive, highInclusive));

// IndexOfAnyExceptInRange should find the first element (all values are outside the invalid range)
Assert.Equal(0, IndexOfAnyExceptInRange(array, lowInclusive, highInclusive));

// LastIndexOfAnyExceptInRange should find the last element (all values are outside the invalid range)
Assert.Equal(length - 1, LastIndexOfAnyExceptInRange(array, lowInclusive, highInclusive));
}
}

[Fact]
public void InvalidRange_SpecificValue_DoesNotMatch()
{
// Test the specific case from the issue: data=[50], low=200, high=100
// This should not match regardless of whether intrinsics are enabled
for (int length = 1; length <= 64; length++)
{
T[] array = new T[length];
for (int i = 0; i < length; i++)
{
array[i] = Create(50);
}

T lowInclusive = Create(200);
T highInclusive = Create(100);

// Value 50 is not in the (invalid) range [200, 100]
Assert.Equal(-1, IndexOfAnyInRange(array, lowInclusive, highInclusive));
Assert.Equal(-1, LastIndexOfAnyInRange(array, lowInclusive, highInclusive));

// Value 50 is outside the (invalid) range [200, 100]
Assert.Equal(0, IndexOfAnyExceptInRange(array, lowInclusive, highInclusive));
Assert.Equal(length - 1, LastIndexOfAnyExceptInRange(array, lowInclusive, highInclusive));
}
}

// Wrappers for {Last}IndexOfAny{Except}InRange that invoke both the Span and ReadOnlySpan overloads,
// ensuring they both produce the same result, and returning that result.
// This avoids needing to code the same call sites twice in all the above tests.
Expand Down
20 changes: 20 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3506,6 +3506,16 @@ internal static int NonPackedIndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T
{
// T must be a type whose comparison operator semantics match that of Vector128/256.

// When highInclusive < lowInclusive, the range is invalid.
// For IndexOfAnyInRange, return -1 (no match).
// For IndexOfAnyExceptInRange, every value is outside the range, so return 0 if length > 0.
if (highInclusive < lowInclusive)
{
return typeof(TNegator) == typeof(DontNegate<T>)
? -1
: (length > 0 ? 0 : -1);
}

if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
{
T rangeInclusive = highInclusive - lowInclusive;
Expand Down Expand Up @@ -3653,6 +3663,16 @@ private static int LastIndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T search
{
// T must be a type whose comparison operator semantics match that of Vector128/256.

// When highInclusive < lowInclusive, the range is invalid.
// For LastIndexOfAnyInRange, return -1 (no match).
// For LastIndexOfAnyExceptInRange, every value is outside the range, so return the last index if length > 0.
if (highInclusive < lowInclusive)
{
return typeof(TNegator) == typeof(DontNegate<T>)
? -1
: (length > 0 ? length - 1 : -1);
}

if (!Vector128.IsHardwareAccelerated || length < Vector128<T>.Count)
{
T rangeInclusive = highInclusive - lowInclusive;
Expand Down
Loading