From 8d6d69a818286b3cce65b02bdb2cfa47bd8c3478 Mon Sep 17 00:00:00 2001 From: ahsonkhan Date: Tue, 21 Mar 2017 15:20:30 -0700 Subject: [PATCH 1/6] Adding Span IndexOf overloads that take multiple bytes. --- src/System.Memory/ref/System.Memory.cs | 5 + .../src/System/SpanExtensions.cs | 52 +++++++ .../src/System/SpanHelpers.byte.cs | 135 ++++++++++++++++++ .../tests/ReadOnlySpan/IndexOf.byte.cs | 101 +++++++++++++ src/System.Memory/tests/Span/IndexOf.byte.cs | 101 +++++++++++++ 5 files changed, 394 insertions(+) diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs index 467fe01f928c..d0f052131f28 100644 --- a/src/System.Memory/ref/System.Memory.cs +++ b/src/System.Memory/ref/System.Memory.cs @@ -97,6 +97,11 @@ public static class SpanExtensions public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : struct, IEquatable { throw null; } public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) { throw null; } + public static int IndexOf(this Span span, byte value0, byte value1) { throw null; } + public static int IndexOf(this Span span, byte value0, byte value1, byte value2) { throw null; } + public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1) { throw null; } + public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1, byte value2) { throw null; } + public static bool SequenceEqual(this Span first, ReadOnlySpan second) where T:struct, IEquatable { throw null; } public static bool SequenceEqual(this Span first, ReadOnlySpan second) { throw null; } diff --git a/src/System.Memory/src/System/SpanExtensions.cs b/src/System.Memory/src/System/SpanExtensions.cs index f8164c3cd56e..b19087b9e90e 100644 --- a/src/System.Memory/src/System/SpanExtensions.cs +++ b/src/System.Memory/src/System/SpanExtensions.cs @@ -125,6 +125,58 @@ public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), span.Length, ref value.DangerousGetPinnableReference(), value.Length); } + /// + /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// + /// The span to search. + /// The first value to search for. + /// The value to search for at the index right after the one where the first one is found. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this Span span, byte value0, byte value1) + { + return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); + } + + /// + /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// + /// The span to search. + /// The first value to search for. + /// The value to search for at the index right after the one where the first one is found. + /// The value to search for at the index right after the one where the second one is found. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this Span span, byte value0, byte value1, byte value2) + { + return -1; + //return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); + } + + /// + /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// + /// The span to search. + /// The first value to search for. + /// The value to search for at the index right after the one where the first one is found. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1) + { + return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); + } + + /// + /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// + /// The span to search. + /// The first value to search for. + /// The value to search for at the index right after the one where the first one is found. + /// The value to search for at the index right after the one where the second one is found. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1, byte value2) + { + return -1; + //return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); + } + /// /// Determines whether two sequences are equal by comparing the elements using IEquatable<T>.Equals(T). /// diff --git a/src/System.Memory/src/System/SpanHelpers.byte.cs b/src/System.Memory/src/System/SpanHelpers.byte.cs index a702ffca5367..cc02a5ce3851 100644 --- a/src/System.Memory/src/System/SpanHelpers.byte.cs +++ b/src/System.Memory/src/System/SpanHelpers.byte.cs @@ -176,6 +176,141 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value, int length) return (int)(byte*)(index + 7); } + public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, int length) + { + Debug.Assert(length >= 0); + + uint uValue0 = value0; // Use uint for comparisions to avoid unnecessary 8->32 extensions + uint uValue1 = value1; // Use uint for comparisions to avoid unnecessary 8->32 extensions + IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations + IntPtr nLength = (IntPtr)(uint)length; +#if !netstandard10 + if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) + { + unchecked + { + int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); + nLength = (IntPtr)(uint)unaligned; + } + } + SequentialScan: +#endif + while ((byte*)nLength >= (byte*)8) + { + nLength -= 8; + + if (uValue0 == Unsafe.Add(ref searchSpace, index) && uValue1 == Unsafe.Add(ref searchSpace, index + 1)) + goto Found; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && uValue1 == Unsafe.Add(ref searchSpace, index + 2)) + goto Found1; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && uValue1 == Unsafe.Add(ref searchSpace, index + 3)) + goto Found2; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && uValue1 == Unsafe.Add(ref searchSpace, index + 4)) + goto Found3; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 4) && uValue1 == Unsafe.Add(ref searchSpace, index + 5)) + goto Found4; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 5) && uValue1 == Unsafe.Add(ref searchSpace, index + 6)) + goto Found5; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 6) && uValue1 == Unsafe.Add(ref searchSpace, index + 7)) + goto Found6; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 7) && uValue1 == Unsafe.Add(ref searchSpace, index + 8)) + goto Found7; + + index += 8; + } + + if ((byte*)nLength >= (byte*)4) + { + nLength -= 4; + + if (uValue0 == Unsafe.Add(ref searchSpace, index) && uValue1 == Unsafe.Add(ref searchSpace, index + 1)) + goto Found; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && uValue1 == Unsafe.Add(ref searchSpace, index + 2)) + goto Found1; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && uValue1 == Unsafe.Add(ref searchSpace, index + 3)) + goto Found2; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && uValue1 == Unsafe.Add(ref searchSpace, index + 4)) + goto Found3; + + index += 4; + } + + while ((byte*)nLength > (byte*)1) + { + nLength -= 1; + + if (uValue0 == Unsafe.Add(ref searchSpace, index) && uValue1 == Unsafe.Add(ref searchSpace, index + 1)) + goto Found; + + index += 1; + } +#if !netstandard10 + if (Vector.IsHardwareAccelerated) + { + if ((int)(byte*)index >= length - 1) + { + goto NotFound; + } + nLength = (IntPtr)(uint)(length - Vector.Count); + // Get comparision Vector + Vector values0 = GetVector(value0); + Vector values1 = GetVector(value1); + do + { + var vData0 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); + var vData1 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index + 1)); + var vMatches = Vector.BitwiseAnd( + Vector.Equals(vData0, values0), + Vector.Equals(vData1, values1)); + + if (!vMatches.Equals(Vector.Zero)) + { + // Found match, reuse Vector values0 to keep register pressure low + values0 = vMatches; + break; + } + index += Vector.Count; + } while ((byte*) nLength > (byte*) index); + + // Found match? Perform secondary search outside out of loop, so above loop body is small + if ((byte*) nLength > (byte*) index) + { + // Find offset of first match + index += LocateFirstFoundByte(values0); + // goto rather than inline return to keep function smaller + goto Found; + } + + if ((int)(byte*)index <= length - 1) + { + unchecked + { + nLength = (IntPtr)(length - (int)(byte*)index); + } + goto SequentialScan; + } + } + NotFound: // Workaround for https://github.com/dotnet/coreclr/issues/9692 +#endif + return -1; + Found: // Workaround for https://github.com/dotnet/coreclr/issues/9692 + return (int)(byte*)index; + Found1: + return (int)(byte*)(index + 1); + Found2: + return (int)(byte*)(index + 2); + Found3: + return (int)(byte*)(index + 3); + Found4: + return (int)(byte*)(index + 4); + Found5: + return (int)(byte*)(index + 5); + Found6: + return (int)(byte*)(index + 6); + Found7: + return (int)(byte*)(index + 7); + } + public static unsafe bool SequenceEqual(ref byte first, ref byte second, int length) { Debug.Assert(length >= 0); diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs index a52b81698db3..d08b8aabaced 100644 --- a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs +++ b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs @@ -70,5 +70,106 @@ public static void MakeSureNoChecksGoOutOfRange_Byte() Assert.Equal(-1, index); } } + + [Fact] + public static void ZeroLengthIndexOfTwo_Byte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOf(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchTwo_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOf(target0, target1); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchTwo_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = (byte)(a[targetIndex + 1] + 1); + int idx = span.IndexOf(target0, target1); + Assert.Equal(-1, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchTwo_Byte() + { + for (int length = 3; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.IndexOf(200, 200); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() + { + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 3]; + a[0] = 99; + a[1] = 98; + a[length + 1] = 99; + a[length + 2] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.IndexOf(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 3]; + a[0] = 99; + a[1] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.IndexOf(99, 99); + Assert.Equal(-1, index); + } + } } } diff --git a/src/System.Memory/tests/Span/IndexOf.byte.cs b/src/System.Memory/tests/Span/IndexOf.byte.cs index aca94dbcbc5a..64ee421b46c0 100644 --- a/src/System.Memory/tests/Span/IndexOf.byte.cs +++ b/src/System.Memory/tests/Span/IndexOf.byte.cs @@ -70,5 +70,106 @@ public static void MakeSureNoChecksGoOutOfRange_Byte() Assert.Equal(-1, index); } } + + [Fact] + public static void ZeroLengthIndexOfTwo_Byte() + { + Span sp = new Span(Array.Empty()); + int idx = sp.IndexOf(0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchTwo_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOf(target0, target1); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchTwo_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = (byte)(a[targetIndex + 1] + 1); + int idx = span.IndexOf(target0, target1); + Assert.Equal(-1, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchTwo_Byte() + { + for (int length = 3; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + + Span span = new Span(a); + int idx = span.IndexOf(200, 200); + Assert.Equal(length - 3, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() + { + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 3]; + a[0] = 99; + a[1] = 98; + a[length + 1] = 99; + a[length + 2] = 98; + Span span = new Span(a, 1, length); + int index = span.IndexOf(99, 98); + Assert.Equal(-1, index); + } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 3]; + a[0] = 99; + a[1] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + Span span = new Span(a, 1, length); + int index = span.IndexOf(99, 99); + Assert.Equal(-1, index); + } + } } } From d542d83a99a7acf0f5e587f1061f95cda2c4cbbf Mon Sep 17 00:00:00 2001 From: ahsonkhan Date: Tue, 21 Mar 2017 16:12:29 -0700 Subject: [PATCH 2/6] Adding IndexOf overload with three byte values. --- .../src/System/SpanExtensions.cs | 6 +- .../src/System/SpanHelpers.byte.cs | 173 +++++++++++++++++- .../tests/ReadOnlySpan/IndexOf.byte.cs | 137 ++++++++++++++ src/System.Memory/tests/Span/IndexOf.byte.cs | 137 ++++++++++++++ 4 files changed, 446 insertions(+), 7 deletions(-) diff --git a/src/System.Memory/src/System/SpanExtensions.cs b/src/System.Memory/src/System/SpanExtensions.cs index b19087b9e90e..a3e7fb2e5a89 100644 --- a/src/System.Memory/src/System/SpanExtensions.cs +++ b/src/System.Memory/src/System/SpanExtensions.cs @@ -147,8 +147,7 @@ public static int IndexOf(this Span span, byte value0, byte value1) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this Span span, byte value0, byte value1, byte value2) { - return -1; - //return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); + return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); } /// @@ -173,8 +172,7 @@ public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1, byte value2) { - return -1; - //return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); + return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); } /// diff --git a/src/System.Memory/src/System/SpanHelpers.byte.cs b/src/System.Memory/src/System/SpanHelpers.byte.cs index cc02a5ce3851..f0d538fb57ec 100644 --- a/src/System.Memory/src/System/SpanHelpers.byte.cs +++ b/src/System.Memory/src/System/SpanHelpers.byte.cs @@ -195,7 +195,7 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, } SequentialScan: #endif - while ((byte*)nLength >= (byte*)8) + while ((byte*)nLength >= (byte*)9) { nLength -= 8; @@ -219,7 +219,7 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, index += 8; } - if ((byte*)nLength >= (byte*)4) + if ((byte*)nLength >= (byte*)5) { nLength -= 4; @@ -235,7 +235,7 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, index += 4; } - while ((byte*)nLength > (byte*)1) + while ((byte*)nLength >= (byte*)2) { nLength -= 1; @@ -311,6 +311,173 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, return (int)(byte*)(index + 7); } + public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, byte value2, int length) + { + Debug.Assert(length >= 0); + + uint uValue0 = value0; // Use uint for comparisions to avoid unnecessary 8->32 extensions + uint uValue1 = value1; // Use uint for comparisions to avoid unnecessary 8->32 extensions + uint uValue2 = value2; // Use uint for comparisions to avoid unnecessary 8->32 extensions + IntPtr index = (IntPtr)0; // Use UIntPtr for arithmetic to avoid unnecessary 64->32->64 truncations + IntPtr nLength = (IntPtr)(uint)length; +#if !netstandard10 + if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) + { + unchecked + { + int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); + nLength = (IntPtr)(uint)unaligned; + } + } + SequentialScan: +#endif + while ((byte*)nLength >= (byte*)10) + { + nLength -= 8; + + if (uValue0 == Unsafe.Add(ref searchSpace, index) && + uValue1 == Unsafe.Add(ref searchSpace, index + 1) && + uValue2 == Unsafe.Add(ref searchSpace, index + 2)) + goto Found; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && + uValue1 == Unsafe.Add(ref searchSpace, index + 2) && + uValue2 == Unsafe.Add(ref searchSpace, index + 3)) + goto Found1; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && + uValue1 == Unsafe.Add(ref searchSpace, index + 3) && + uValue2 == Unsafe.Add(ref searchSpace, index + 4)) + goto Found2; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && + uValue1 == Unsafe.Add(ref searchSpace, index + 4) && + uValue2 == Unsafe.Add(ref searchSpace, index + 5)) + goto Found3; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 4) && + uValue1 == Unsafe.Add(ref searchSpace, index + 5) && + uValue2 == Unsafe.Add(ref searchSpace, index + 6)) + goto Found4; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 5) && + uValue1 == Unsafe.Add(ref searchSpace, index + 6) && + uValue2 == Unsafe.Add(ref searchSpace, index + 7)) + goto Found5; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 6) && + uValue1 == Unsafe.Add(ref searchSpace, index + 7) && + uValue2 == Unsafe.Add(ref searchSpace, index + 8)) + goto Found6; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 7) && + uValue1 == Unsafe.Add(ref searchSpace, index + 8) && + uValue2 == Unsafe.Add(ref searchSpace, index + 9)) + goto Found7; + + index += 8; + } + + if ((byte*)nLength >= (byte*)6) + { + nLength -= 4; + + if (uValue0 == Unsafe.Add(ref searchSpace, index) && + uValue1 == Unsafe.Add(ref searchSpace, index + 1) && + uValue2 == Unsafe.Add(ref searchSpace, index + 2)) + goto Found; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && + uValue1 == Unsafe.Add(ref searchSpace, index + 2) && + uValue2 == Unsafe.Add(ref searchSpace, index + 3)) + goto Found1; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && + uValue1 == Unsafe.Add(ref searchSpace, index + 3) && + uValue2 == Unsafe.Add(ref searchSpace, index + 4)) + goto Found2; + if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && + uValue1 == Unsafe.Add(ref searchSpace, index + 4) && + uValue2 == Unsafe.Add(ref searchSpace, index + 5)) + goto Found3; + + index += 4; + } + + while ((byte*)nLength >= (byte*)3) + { + nLength -= 1; + + if (uValue0 == Unsafe.Add(ref searchSpace, index) && + uValue1 == Unsafe.Add(ref searchSpace, index + 1) && + uValue2 == Unsafe.Add(ref searchSpace, index + 2)) + goto Found; + + index += 1; + } +#if !netstandard10 + if (Vector.IsHardwareAccelerated) + { + if ((int)(byte*)index >= length - 2) + { + goto NotFound; + } + nLength = (IntPtr)(uint)(length - Vector.Count); + // Get comparision Vector + Vector values0 = GetVector(value0); + Vector values1 = GetVector(value1); + Vector values2 = GetVector(value2); + do + { + var vData0 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); + var vData1 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index + 1)); + var vData2 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index + 2)); + + var vMatches = Vector.BitwiseAnd( + Vector.BitwiseAnd( + Vector.Equals(vData0, values0), + Vector.Equals(vData1, values1)), + Vector.Equals(vData2, values2)); + + if (!vMatches.Equals(Vector.Zero)) + { + // Found match, reuse Vector values0 to keep register pressure low + values0 = vMatches; + break; + } + index += Vector.Count; + } while ((byte*)nLength > (byte*)index); + + // Found match? Perform secondary search outside out of loop, so above loop body is small + if ((byte*)nLength > (byte*)index) + { + // Find offset of first match + index += LocateFirstFoundByte(values0); + // goto rather than inline return to keep function smaller + goto Found; + } + + if ((int)(byte*)index <= length - 2) + { + unchecked + { + nLength = (IntPtr)(length - (int)(byte*)index); + } + goto SequentialScan; + } + } + NotFound: // Workaround for https://github.com/dotnet/coreclr/issues/9692 +#endif + return -1; + Found: // Workaround for https://github.com/dotnet/coreclr/issues/9692 + return (int)(byte*)index; + Found1: + return (int)(byte*)(index + 1); + Found2: + return (int)(byte*)(index + 2); + Found3: + return (int)(byte*)(index + 3); + Found4: + return (int)(byte*)(index + 4); + Found5: + return (int)(byte*)(index + 5); + Found6: + return (int)(byte*)(index + 6); + Found7: + return (int)(byte*)(index + 7); + } + public static unsafe bool SequenceEqual(ref byte first, ref byte second, int length) { Debug.Assert(length >= 0); diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs index d08b8aabaced..142d55ef2c25 100644 --- a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs +++ b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs @@ -170,6 +170,143 @@ public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() int index = span.IndexOf(99, 99); Assert.Equal(-1, index); } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 3]; + a[0] = 99; + a[1] = 99; + a[length] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.IndexOf(99, 98); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfThree_Byte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + int idx = sp.IndexOf(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchThree_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOf(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchThree_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = (byte)(a[targetIndex + 1] + 1); + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOf(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchThree_Byte() + { + for (int length = 4; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + int idx = span.IndexOf(200, 200, 200); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeThree_Byte() + { + for (int length = 4; length < 5; length++) + { + byte[] a = new byte[length + 4]; + a[0] = 99; + a[1] = 99; + a[2] = 98; + a[length] = 99; + a[length + 1] = 99; + a[length + 2] = 98; + a[length + 3] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.IndexOf(99, 99, 98); + Assert.Equal(-1, index); + } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 4]; + a[0] = 99; + a[1] = 99; + a[2] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + a[length + 3] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.IndexOf(99, 99, 99); + Assert.Equal(-1, index); + } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 4]; + a[0] = 99; + a[1] = 99; + a[2] = 99; + a[length] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + a[length + 3] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length); + int index = span.IndexOf(99, 99, 98); + Assert.Equal(-1, index); + } } } } diff --git a/src/System.Memory/tests/Span/IndexOf.byte.cs b/src/System.Memory/tests/Span/IndexOf.byte.cs index 64ee421b46c0..ce06d9176fb6 100644 --- a/src/System.Memory/tests/Span/IndexOf.byte.cs +++ b/src/System.Memory/tests/Span/IndexOf.byte.cs @@ -170,6 +170,143 @@ public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() int index = span.IndexOf(99, 99); Assert.Equal(-1, index); } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 3]; + a[0] = 99; + a[1] = 99; + a[length] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + Span span = new Span(a, 1, length); + int index = span.IndexOf(99, 98); + Assert.Equal(-1, index); + } + } + + [Fact] + public static void ZeroLengthIndexOfThree_Byte() + { + Span sp = new Span(Array.Empty()); + int idx = sp.IndexOf(0, 0, 0); + Assert.Equal(-1, idx); + } + + [Fact] + public static void TestMatchThree_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = a[targetIndex + 1]; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOf(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + } + } + + [Fact] + public static void TestNoMatchThree_Byte() + { + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = (byte)(a[targetIndex + 1] + 1); + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOf(target0, target1, target2); + Assert.Equal(-1, idx); + } + } + } + + [Fact] + public static void TestMultipleMatchThree_Byte() + { + for (int length = 4; length < 32; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + + Span span = new Span(a); + int idx = span.IndexOf(200, 200, 200); + Assert.Equal(length - 4, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeThree_Byte() + { + for (int length = 4; length < 5; length++) + { + byte[] a = new byte[length + 4]; + a[0] = 99; + a[1] = 99; + a[2] = 98; + a[length] = 99; + a[length + 1] = 99; + a[length + 2] = 98; + a[length + 3] = 98; + Span span = new Span(a, 1, length); + int index = span.IndexOf(99, 99, 98); + Assert.Equal(-1, index); + } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 4]; + a[0] = 99; + a[1] = 99; + a[2] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + a[length + 3] = 99; + Span span = new Span(a, 1, length); + int index = span.IndexOf(99, 99, 99); + Assert.Equal(-1, index); + } + + for (int length = 0; length < 100; length++) + { + byte[] a = new byte[length + 4]; + a[0] = 99; + a[1] = 99; + a[2] = 99; + a[length] = 99; + a[length + 1] = 99; + a[length + 2] = 99; + a[length + 3] = 99; + Span span = new Span(a, 1, length); + int index = span.IndexOf(99, 99, 98); + Assert.Equal(-1, index); + } } } } From 865ddf22718c24b50ca12787800fbce17f738596 Mon Sep 17 00:00:00 2001 From: ahsonkhan Date: Wed, 22 Mar 2017 15:03:49 -0700 Subject: [PATCH 3/6] Changing IndexOf overload to IndexOfAny and fixing tests/logic. --- src/System.Memory/ref/System.Memory.cs | 8 +- .../src/System/SpanExtensions.cs | 45 ++-- .../src/System/SpanHelpers.byte.cs | 143 +++++++------ .../tests/ReadOnlySpan/IndexOf.byte.cs | 188 ++++++++++------- src/System.Memory/tests/Span/IndexOf.byte.cs | 192 +++++++++++------- 5 files changed, 330 insertions(+), 246 deletions(-) diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs index d0f052131f28..02398eb6c704 100644 --- a/src/System.Memory/ref/System.Memory.cs +++ b/src/System.Memory/ref/System.Memory.cs @@ -97,10 +97,10 @@ public static class SpanExtensions public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : struct, IEquatable { throw null; } public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) { throw null; } - public static int IndexOf(this Span span, byte value0, byte value1) { throw null; } - public static int IndexOf(this Span span, byte value0, byte value1, byte value2) { throw null; } - public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1) { throw null; } - public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1, byte value2) { throw null; } + public static int IndexOfAny(this Span span, byte value0, byte value1) { throw null; } + public static int IndexOfAny(this Span span, byte value0, byte value1, byte value2) { throw null; } + public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1) { throw null; } + public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1, byte value2) { throw null; } public static bool SequenceEqual(this Span first, ReadOnlySpan second) where T:struct, IEquatable { throw null; } public static bool SequenceEqual(this Span first, ReadOnlySpan second) { throw null; } diff --git a/src/System.Memory/src/System/SpanExtensions.cs b/src/System.Memory/src/System/SpanExtensions.cs index a3e7fb2e5a89..439e111cc7ea 100644 --- a/src/System.Memory/src/System/SpanExtensions.cs +++ b/src/System.Memory/src/System/SpanExtensions.cs @@ -125,54 +125,55 @@ public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), span.Length, ref value.DangerousGetPinnableReference(), value.Length); } + /// - /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. - /// The first value to search for. - /// The value to search for at the index right after the one where the first one is found. + /// One of the values to search for. + /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this Span span, byte value0, byte value1) + public static int IndexOfAny(this Span span, byte value0, byte value1) { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); + return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); } /// - /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. - /// The first value to search for. - /// The value to search for at the index right after the one where the first one is found. - /// The value to search for at the index right after the one where the second one is found. + /// One of the values to search for. + /// One of the values to search for. + /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this Span span, byte value0, byte value1, byte value2) + public static int IndexOfAny(this Span span, byte value0, byte value1, byte value2) { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); + return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); } /// - /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. - /// The first value to search for. - /// The value to search for at the index right after the one where the first one is found. + /// One of the values to search for. + /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1) + public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1) { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); + return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, span.Length); } /// - /// Searches for the specified values and returns the index of the first occurrence where all of them appear in sequence. If not found, returns -1. + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. - /// The first value to search for. - /// The value to search for at the index right after the one where the first one is found. - /// The value to search for at the index right after the one where the second one is found. + /// One of the values to search for. + /// One of the values to search for. + /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this ReadOnlySpan span, byte value0, byte value1, byte value2) + public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1, byte value2) { - return SpanHelpers.IndexOf(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); + return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); } /// diff --git a/src/System.Memory/src/System/SpanHelpers.byte.cs b/src/System.Memory/src/System/SpanHelpers.byte.cs index f0d538fb57ec..5f26e2ab9784 100644 --- a/src/System.Memory/src/System/SpanHelpers.byte.cs +++ b/src/System.Memory/src/System/SpanHelpers.byte.cs @@ -176,7 +176,7 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value, int length) return (int)(byte*)(index + 7); } - public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, int length) + public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, int length) { Debug.Assert(length >= 0); @@ -195,51 +195,65 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, } SequentialScan: #endif - while ((byte*)nLength >= (byte*)9) + uint lookUp; + while ((byte*)nLength >= (byte*)8) { nLength -= 8; - if (uValue0 == Unsafe.Add(ref searchSpace, index) && uValue1 == Unsafe.Add(ref searchSpace, index + 1)) + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && uValue1 == Unsafe.Add(ref searchSpace, index + 2)) + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found1; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && uValue1 == Unsafe.Add(ref searchSpace, index + 3)) + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found2; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && uValue1 == Unsafe.Add(ref searchSpace, index + 4)) + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found3; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 4) && uValue1 == Unsafe.Add(ref searchSpace, index + 5)) + lookUp = Unsafe.Add(ref searchSpace, index + 4); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found4; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 5) && uValue1 == Unsafe.Add(ref searchSpace, index + 6)) + lookUp = Unsafe.Add(ref searchSpace, index + 5); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found5; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 6) && uValue1 == Unsafe.Add(ref searchSpace, index + 7)) + lookUp = Unsafe.Add(ref searchSpace, index + 6); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found6; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 7) && uValue1 == Unsafe.Add(ref searchSpace, index + 8)) + lookUp = Unsafe.Add(ref searchSpace, index + 7); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found7; index += 8; } - if ((byte*)nLength >= (byte*)5) + if ((byte*)nLength >= (byte*)4) { nLength -= 4; - if (uValue0 == Unsafe.Add(ref searchSpace, index) && uValue1 == Unsafe.Add(ref searchSpace, index + 1)) + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && uValue1 == Unsafe.Add(ref searchSpace, index + 2)) + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found1; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && uValue1 == Unsafe.Add(ref searchSpace, index + 3)) + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found2; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && uValue1 == Unsafe.Add(ref searchSpace, index + 4)) + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found3; index += 4; } - while ((byte*)nLength >= (byte*)2) + while ((byte*)nLength > (byte*)0) { nLength -= 1; - if (uValue0 == Unsafe.Add(ref searchSpace, index) && uValue1 == Unsafe.Add(ref searchSpace, index + 1)) + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp) goto Found; index += 1; @@ -257,11 +271,10 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, Vector values1 = GetVector(value1); do { - var vData0 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); - var vData1 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index + 1)); - var vMatches = Vector.BitwiseAnd( - Vector.Equals(vData0, values0), - Vector.Equals(vData1, values1)); + var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); + var vMatches = Vector.BitwiseOr( + Vector.Equals(vData, values0), + Vector.Equals(vData, values1)); if (!vMatches.Equals(Vector.Zero)) { @@ -311,7 +324,7 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, return (int)(byte*)(index + 7); } - public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, byte value2, int length) + public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length) { Debug.Assert(length >= 0); @@ -331,77 +344,65 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, } SequentialScan: #endif - while ((byte*)nLength >= (byte*)10) + uint lookUp; + while ((byte*)nLength >= (byte*)8) { nLength -= 8; - if (uValue0 == Unsafe.Add(ref searchSpace, index) && - uValue1 == Unsafe.Add(ref searchSpace, index + 1) && - uValue2 == Unsafe.Add(ref searchSpace, index + 2)) + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && - uValue1 == Unsafe.Add(ref searchSpace, index + 2) && - uValue2 == Unsafe.Add(ref searchSpace, index + 3)) + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found1; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && - uValue1 == Unsafe.Add(ref searchSpace, index + 3) && - uValue2 == Unsafe.Add(ref searchSpace, index + 4)) + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found2; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && - uValue1 == Unsafe.Add(ref searchSpace, index + 4) && - uValue2 == Unsafe.Add(ref searchSpace, index + 5)) + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found3; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 4) && - uValue1 == Unsafe.Add(ref searchSpace, index + 5) && - uValue2 == Unsafe.Add(ref searchSpace, index + 6)) + lookUp = Unsafe.Add(ref searchSpace, index + 4); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found4; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 5) && - uValue1 == Unsafe.Add(ref searchSpace, index + 6) && - uValue2 == Unsafe.Add(ref searchSpace, index + 7)) + lookUp = Unsafe.Add(ref searchSpace, index + 5); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found5; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 6) && - uValue1 == Unsafe.Add(ref searchSpace, index + 7) && - uValue2 == Unsafe.Add(ref searchSpace, index + 8)) + lookUp = Unsafe.Add(ref searchSpace, index + 6); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found6; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 7) && - uValue1 == Unsafe.Add(ref searchSpace, index + 8) && - uValue2 == Unsafe.Add(ref searchSpace, index + 9)) + lookUp = Unsafe.Add(ref searchSpace, index + 7); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found7; index += 8; } - if ((byte*)nLength >= (byte*)6) + if ((byte*)nLength >= (byte*)4) { nLength -= 4; - if (uValue0 == Unsafe.Add(ref searchSpace, index) && - uValue1 == Unsafe.Add(ref searchSpace, index + 1) && - uValue2 == Unsafe.Add(ref searchSpace, index + 2)) + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 1) && - uValue1 == Unsafe.Add(ref searchSpace, index + 2) && - uValue2 == Unsafe.Add(ref searchSpace, index + 3)) + lookUp = Unsafe.Add(ref searchSpace, index + 1); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found1; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 2) && - uValue1 == Unsafe.Add(ref searchSpace, index + 3) && - uValue2 == Unsafe.Add(ref searchSpace, index + 4)) + lookUp = Unsafe.Add(ref searchSpace, index + 2); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found2; - if (uValue0 == Unsafe.Add(ref searchSpace, index + 3) && - uValue1 == Unsafe.Add(ref searchSpace, index + 4) && - uValue2 == Unsafe.Add(ref searchSpace, index + 5)) + lookUp = Unsafe.Add(ref searchSpace, index + 3); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found3; index += 4; } - while ((byte*)nLength >= (byte*)3) + while ((byte*)nLength > (byte*)0) { nLength -= 1; - if (uValue0 == Unsafe.Add(ref searchSpace, index) && - uValue1 == Unsafe.Add(ref searchSpace, index + 1) && - uValue2 == Unsafe.Add(ref searchSpace, index + 2)) + lookUp = Unsafe.Add(ref searchSpace, index); + if (uValue0 == lookUp || uValue1 == lookUp || uValue2 == lookUp) goto Found; index += 1; @@ -420,15 +421,13 @@ public static unsafe int IndexOf(ref byte searchSpace, byte value0, byte value1, Vector values2 = GetVector(value2); do { - var vData0 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); - var vData1 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index + 1)); - var vData2 = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index + 2)); + var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); - var vMatches = Vector.BitwiseAnd( + var vMatches = Vector.BitwiseOr( Vector.BitwiseAnd( - Vector.Equals(vData0, values0), - Vector.Equals(vData1, values1)), - Vector.Equals(vData2, values2)); + Vector.Equals(vData, values0), + Vector.Equals(vData, values1)), + Vector.Equals(vData, values2)); if (!vMatches.Equals(Vector.Zero)) { diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs index 142d55ef2c25..fed45e6e43bb 100644 --- a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs +++ b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs @@ -75,10 +75,34 @@ public static void MakeSureNoChecksGoOutOfRange_Byte() public static void ZeroLengthIndexOfTwo_Byte() { ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); - int idx = sp.IndexOf(0, 0); + int idx = sp.IndexOfAny(0, 0); Assert.Equal(-1, idx); } + [Fact] + public static void DefaultFilledIndexOfTwo_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = { default(byte), 99 }; + + for (int i = 0; i < length; i++) + { + int index; + index = rnd.Next(0, 2) == 0 ? 0 : 1; + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + [Fact] public static void TestMatchTwo_Byte() { @@ -91,13 +115,29 @@ public static void TestMatchTwo_Byte() } ReadOnlySpan span = new ReadOnlySpan(a); + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 99; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) { byte target0 = a[targetIndex]; byte target1 = a[targetIndex + 1]; - int idx = span.IndexOf(target0, target1); + int idx = span.IndexOfAny(target0, target1); Assert.Equal(targetIndex, idx); } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = 99; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } } } @@ -115,9 +155,9 @@ public static void TestNoMatchTwo_Byte() for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) { - byte target0 = a[targetIndex]; - byte target1 = (byte)(a[targetIndex + 1] + 1); - int idx = span.IndexOf(target0, target1); + byte target0 = 98; + byte target1 = 99; + int idx = span.IndexOfAny(target0, target1); Assert.Equal(-1, idx); } } @@ -139,7 +179,7 @@ public static void TestMultipleMatchTwo_Byte() a[length - 3] = 200; ReadOnlySpan span = new ReadOnlySpan(a); - int idx = span.IndexOf(200, 200); + int idx = span.IndexOfAny(200, 200); Assert.Equal(length - 3, idx); } } @@ -147,40 +187,23 @@ public static void TestMultipleMatchTwo_Byte() [Fact] public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() { - for (int length = 0; length < 100; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 3]; - a[0] = 99; - a[1] = 98; - a[length + 1] = 99; - a[length + 2] = 98; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length); - int index = span.IndexOf(99, 98); - Assert.Equal(-1, index); - } - - for (int length = 0; length < 100; length++) - { - byte[] a = new byte[length + 3]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[length + 1] = 99; - a[length + 2] = 99; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length); - int index = span.IndexOf(99, 99); + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 98); Assert.Equal(-1, index); } - for (int length = 0; length < 100; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 3]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[length] = 99; a[length + 1] = 99; - a[length + 2] = 99; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length); - int index = span.IndexOf(99, 98); + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 99); Assert.Equal(-1, index); } } @@ -189,10 +212,35 @@ public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() public static void ZeroLengthIndexOfThree_Byte() { ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); - int idx = sp.IndexOf(0, 0, 0); + int idx = sp.IndexOfAny(0, 0, 0); Assert.Equal(-1, idx); } + [Fact] + public static void DefaultFilledIndexOfThree_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = { default(byte), 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index; + index = rnd.Next(0, 3); + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + byte traget2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, traget2); + Assert.Equal(0, idx); + } + } + } + [Fact] public static void TestMatchThree_Byte() { @@ -205,14 +253,32 @@ public static void TestMatchThree_Byte() } ReadOnlySpan span = new ReadOnlySpan(a); + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 99; + byte target2 = 99; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) { byte target0 = a[targetIndex]; byte target1 = a[targetIndex + 1]; byte target2 = a[targetIndex + 2]; - int idx = span.IndexOf(target0, target1, target2); + int idx = span.IndexOfAny(target0, target1, target2); Assert.Equal(targetIndex, idx); } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = 99; + byte target1 = 99; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } } } @@ -228,12 +294,12 @@ public static void TestNoMatchThree_Byte() } ReadOnlySpan span = new ReadOnlySpan(a); - for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) { - byte target0 = a[targetIndex]; - byte target1 = (byte)(a[targetIndex + 1] + 1); - byte target2 = a[targetIndex + 2]; - int idx = span.IndexOf(target0, target1, target2); + byte target0 = 98; + byte target1 = 99; + byte target2 = 100; + int idx = span.IndexOfAny(target0, target1, target2); Assert.Equal(-1, idx); } } @@ -256,7 +322,7 @@ public static void TestMultipleMatchThree_Byte() a[length - 4] = 200; ReadOnlySpan span = new ReadOnlySpan(a); - int idx = span.IndexOf(200, 200, 200); + int idx = span.IndexOfAny(200, 200, 200); Assert.Equal(length - 4, idx); } } @@ -264,47 +330,23 @@ public static void TestMultipleMatchThree_Byte() [Fact] public static void MakeSureNoChecksGoOutOfRangeThree_Byte() { - for (int length = 4; length < 5; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 4]; - a[0] = 99; - a[1] = 99; - a[2] = 98; - a[length] = 99; - a[length + 1] = 99; - a[length + 2] = 98; - a[length + 3] = 98; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length); - int index = span.IndexOf(99, 99, 98); - Assert.Equal(-1, index); - } - - for (int length = 0; length < 100; length++) - { - byte[] a = new byte[length + 4]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[2] = 99; - a[length + 1] = 99; - a[length + 2] = 99; - a[length + 3] = 99; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length); - int index = span.IndexOf(99, 99, 99); + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 98, 99); Assert.Equal(-1, index); } - for (int length = 0; length < 100; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 4]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[2] = 99; - a[length] = 99; a[length + 1] = 99; - a[length + 2] = 99; - a[length + 3] = 99; - ReadOnlySpan span = new ReadOnlySpan(a, 1, length); - int index = span.IndexOf(99, 99, 98); + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + int index = span.IndexOfAny(99, 99, 99); Assert.Equal(-1, index); } } diff --git a/src/System.Memory/tests/Span/IndexOf.byte.cs b/src/System.Memory/tests/Span/IndexOf.byte.cs index ce06d9176fb6..0e555aaee797 100644 --- a/src/System.Memory/tests/Span/IndexOf.byte.cs +++ b/src/System.Memory/tests/Span/IndexOf.byte.cs @@ -75,10 +75,34 @@ public static void MakeSureNoChecksGoOutOfRange_Byte() public static void ZeroLengthIndexOfTwo_Byte() { Span sp = new Span(Array.Empty()); - int idx = sp.IndexOf(0, 0); + int idx = sp.IndexOfAny(0, 0); Assert.Equal(-1, idx); } + [Fact] + public static void DefaultFilledIndexOfTwo_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + byte[] targets = { default(byte), 99 }; + + for (int i = 0; i < length; i++) + { + int index; + index = rnd.Next(0, 2) == 0 ? 0 : 1; + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(0, idx); + } + } + } + [Fact] public static void TestMatchTwo_Byte() { @@ -91,13 +115,29 @@ public static void TestMatchTwo_Byte() } Span span = new Span(a); + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 99; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex, idx); + } + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) { byte target0 = a[targetIndex]; byte target1 = a[targetIndex + 1]; - int idx = span.IndexOf(target0, target1); + int idx = span.IndexOfAny(target0, target1); Assert.Equal(targetIndex, idx); } + + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) + { + byte target0 = 99; + byte target1 = a[targetIndex + 1]; + int idx = span.IndexOfAny(target0, target1); + Assert.Equal(targetIndex + 1, idx); + } } } @@ -115,9 +155,9 @@ public static void TestNoMatchTwo_Byte() for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) { - byte target0 = a[targetIndex]; - byte target1 = (byte)(a[targetIndex + 1] + 1); - int idx = span.IndexOf(target0, target1); + byte target0 = 98; + byte target1 = 99; + int idx = span.IndexOfAny(target0, target1); Assert.Equal(-1, idx); } } @@ -139,7 +179,7 @@ public static void TestMultipleMatchTwo_Byte() a[length - 3] = 200; Span span = new Span(a); - int idx = span.IndexOf(200, 200); + int idx = span.IndexOfAny(200, 200); Assert.Equal(length - 3, idx); } } @@ -147,52 +187,60 @@ public static void TestMultipleMatchTwo_Byte() [Fact] public static void MakeSureNoChecksGoOutOfRangeTwo_Byte() { - for (int length = 0; length < 100; length++) - { - byte[] a = new byte[length + 3]; - a[0] = 99; - a[1] = 98; - a[length + 1] = 99; - a[length + 2] = 98; - Span span = new Span(a, 1, length); - int index = span.IndexOf(99, 98); - Assert.Equal(-1, index); - } - - for (int length = 0; length < 100; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 3]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[length + 1] = 99; - a[length + 2] = 99; - Span span = new Span(a, 1, length); - int index = span.IndexOf(99, 99); + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 98); Assert.Equal(-1, index); } - for (int length = 0; length < 100; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 3]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[length] = 99; a[length + 1] = 99; - a[length + 2] = 99; - Span span = new Span(a, 1, length); - int index = span.IndexOf(99, 98); + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 99); Assert.Equal(-1, index); } } - + [Fact] public static void ZeroLengthIndexOfThree_Byte() { Span sp = new Span(Array.Empty()); - int idx = sp.IndexOf(0, 0, 0); + int idx = sp.IndexOfAny(0, 0, 0); Assert.Equal(-1, idx); } + [Fact] + public static void DefaultFilledIndexOfThree_Byte() + { + Random rnd = new Random(42); + + for (int length = 0; length < 32; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + byte[] targets = { default(byte), 99, 98 }; + + for (int i = 0; i < length; i++) + { + int index; + index = rnd.Next(0, 3); + byte target0 = targets[index]; + byte target1 = targets[(index + 1) % 2]; + byte traget2 = targets[(index + 1) % 3]; + int idx = span.IndexOfAny(target0, target1, traget2); + Assert.Equal(0, idx); + } + } + } + [Fact] public static void TestMatchThree_Byte() { @@ -205,14 +253,32 @@ public static void TestMatchThree_Byte() } Span span = new Span(a); + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = a[targetIndex]; + byte target1 = 99; + byte target2 = 99; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex, idx); + } + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) { byte target0 = a[targetIndex]; byte target1 = a[targetIndex + 1]; byte target2 = a[targetIndex + 2]; - int idx = span.IndexOf(target0, target1, target2); + int idx = span.IndexOfAny(target0, target1, target2); Assert.Equal(targetIndex, idx); } + + for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + { + byte target0 = 99; + byte target1 = 99; + byte target2 = a[targetIndex + 2]; + int idx = span.IndexOfAny(target0, target1, target2); + Assert.Equal(targetIndex + 2, idx); + } } } @@ -228,12 +294,12 @@ public static void TestNoMatchThree_Byte() } Span span = new Span(a); - for (int targetIndex = 0; targetIndex < length - 2; targetIndex++) + for (int targetIndex = 0; targetIndex < length - 1; targetIndex++) { - byte target0 = a[targetIndex]; - byte target1 = (byte)(a[targetIndex + 1] + 1); - byte target2 = a[targetIndex + 2]; - int idx = span.IndexOf(target0, target1, target2); + byte target0 = 98; + byte target1 = 99; + byte target2 = 100; + int idx = span.IndexOfAny(target0, target1, target2); Assert.Equal(-1, idx); } } @@ -249,14 +315,14 @@ public static void TestMultipleMatchThree_Byte() { a[i] = (byte)(i + 1); } - + a[length - 1] = 200; a[length - 2] = 200; a[length - 3] = 200; a[length - 4] = 200; Span span = new Span(a); - int idx = span.IndexOf(200, 200, 200); + int idx = span.IndexOfAny(200, 200, 200); Assert.Equal(length - 4, idx); } } @@ -264,47 +330,23 @@ public static void TestMultipleMatchThree_Byte() [Fact] public static void MakeSureNoChecksGoOutOfRangeThree_Byte() { - for (int length = 4; length < 5; length++) - { - byte[] a = new byte[length + 4]; - a[0] = 99; - a[1] = 99; - a[2] = 98; - a[length] = 99; - a[length + 1] = 99; - a[length + 2] = 98; - a[length + 3] = 98; - Span span = new Span(a, 1, length); - int index = span.IndexOf(99, 99, 98); - Assert.Equal(-1, index); - } - - for (int length = 0; length < 100; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 4]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[2] = 99; - a[length + 1] = 99; - a[length + 2] = 99; - a[length + 3] = 99; - Span span = new Span(a, 1, length); - int index = span.IndexOf(99, 99, 99); + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 98, 99); Assert.Equal(-1, index); } - for (int length = 0; length < 100; length++) + for (int length = 1; length < 100; length++) { - byte[] a = new byte[length + 4]; + byte[] a = new byte[length + 2]; a[0] = 99; - a[1] = 99; - a[2] = 99; - a[length] = 99; a[length + 1] = 99; - a[length + 2] = 99; - a[length + 3] = 99; - Span span = new Span(a, 1, length); - int index = span.IndexOf(99, 99, 98); + Span span = new Span(a, 1, length - 1); + int index = span.IndexOfAny(99, 99, 99); Assert.Equal(-1, index); } } From e91d1d28026939b9fcea18d3982dad6f642a1df0 Mon Sep 17 00:00:00 2001 From: ahsonkhan Date: Wed, 22 Mar 2017 19:09:21 -0700 Subject: [PATCH 4/6] Fixing spacing. --- src/System.Memory/src/System/SpanHelpers.byte.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/System.Memory/src/System/SpanHelpers.byte.cs b/src/System.Memory/src/System/SpanHelpers.byte.cs index 5f26e2ab9784..42b9562e1cc4 100644 --- a/src/System.Memory/src/System/SpanHelpers.byte.cs +++ b/src/System.Memory/src/System/SpanHelpers.byte.cs @@ -273,8 +273,8 @@ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte valu { var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); var vMatches = Vector.BitwiseOr( - Vector.Equals(vData, values0), - Vector.Equals(vData, values1)); + Vector.Equals(vData, values0), + Vector.Equals(vData, values1)); if (!vMatches.Equals(Vector.Zero)) { @@ -283,10 +283,10 @@ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte valu break; } index += Vector.Count; - } while ((byte*) nLength > (byte*) index); + } while ((byte*)nLength > (byte*)index); // Found match? Perform secondary search outside out of loop, so above loop body is small - if ((byte*) nLength > (byte*) index) + if ((byte*)nLength > (byte*)index) { // Find offset of first match index += LocateFirstFoundByte(values0); @@ -424,10 +424,10 @@ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte valu var vData = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, index)); var vMatches = Vector.BitwiseOr( - Vector.BitwiseAnd( - Vector.Equals(vData, values0), - Vector.Equals(vData, values1)), - Vector.Equals(vData, values2)); + Vector.BitwiseAnd( + Vector.Equals(vData, values0), + Vector.Equals(vData, values1)), + Vector.Equals(vData, values2)); if (!vMatches.Equals(Vector.Zero)) { From 7a3d3e0bac008151c0e861acf7697e42a4cf5e84 Mon Sep 17 00:00:00 2001 From: ahsonkhan Date: Fri, 24 Mar 2017 21:21:44 -0700 Subject: [PATCH 5/6] Adding IndexOfAny that takes a Span of lookup values. --- src/System.Memory/ref/System.Memory.cs | 2 + .../src/System/SpanExtensions.cs | 22 ++ .../src/System/SpanHelpers.byte.cs | 20 ++ .../tests/ReadOnlySpan/IndexOf.byte.cs | 191 ++++++++++++++++++ src/System.Memory/tests/Span/IndexOf.byte.cs | 191 ++++++++++++++++++ 5 files changed, 426 insertions(+) diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs index 02398eb6c704..c06eea658634 100644 --- a/src/System.Memory/ref/System.Memory.cs +++ b/src/System.Memory/ref/System.Memory.cs @@ -99,8 +99,10 @@ public static class SpanExtensions public static int IndexOfAny(this Span span, byte value0, byte value1) { throw null; } public static int IndexOfAny(this Span span, byte value0, byte value1, byte value2) { throw null; } + public static int IndexOfAny(this Span span, ReadOnlySpan values) { throw null; } public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1) { throw null; } public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte value1, byte value2) { throw null; } + public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) { throw null; } public static bool SequenceEqual(this Span first, ReadOnlySpan second) where T:struct, IEquatable { throw null; } public static bool SequenceEqual(this Span first, ReadOnlySpan second) { throw null; } diff --git a/src/System.Memory/src/System/SpanExtensions.cs b/src/System.Memory/src/System/SpanExtensions.cs index 439e111cc7ea..1d3d94534790 100644 --- a/src/System.Memory/src/System/SpanExtensions.cs +++ b/src/System.Memory/src/System/SpanExtensions.cs @@ -151,6 +151,17 @@ public static int IndexOfAny(this Span span, byte value0, byte value1, byt return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); } + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// The set of values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this Span span, ReadOnlySpan values) + { + return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), span.Length, ref values.DangerousGetPinnableReference(), values.Length); + } + /// /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// @@ -176,6 +187,17 @@ public static int IndexOfAny(this ReadOnlySpan span, byte value0, byte val return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length); } + /// + /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. + /// + /// The span to search. + /// The set of values to search for. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) + { + return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), span.Length, ref values.DangerousGetPinnableReference(), values.Length); + } + /// /// Determines whether two sequences are equal by comparing the elements using IEquatable<T>.Equals(T). /// diff --git a/src/System.Memory/src/System/SpanHelpers.byte.cs b/src/System.Memory/src/System/SpanHelpers.byte.cs index 42b9562e1cc4..1cf13d9a2899 100644 --- a/src/System.Memory/src/System/SpanHelpers.byte.cs +++ b/src/System.Memory/src/System/SpanHelpers.byte.cs @@ -48,6 +48,26 @@ public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte return -1; } + public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) + { + Debug.Assert(searchSpaceLength >= 0); + Debug.Assert(valueLength >= 0); + + if (valueLength == 0) + return 0; // A zero-length sequence is always treated as "found" at the start of the search space. + + int index = -1; + for (int i = 0; i < valueLength; i++) + { + var tempIndex = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); + if (tempIndex != -1) + { + index = (index == -1 || index > tempIndex) ? tempIndex : index; + } + } + return index; + } + public static unsafe int IndexOf(ref byte searchSpace, byte value, int length) { Debug.Assert(length >= 0); diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs index fed45e6e43bb..31caef6d19dd 100644 --- a/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs +++ b/src/System.Memory/tests/ReadOnlySpan/IndexOf.byte.cs @@ -350,5 +350,196 @@ public static void MakeSureNoChecksGoOutOfRangeThree_Byte() Assert.Equal(-1, index); } } + + [Fact] + public static void ZeroLengthIndexOfMany_Byte() + { + ReadOnlySpan sp = new ReadOnlySpan(Array.Empty()); + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, 0 }); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new byte[] { }); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + ReadOnlySpan span = new ReadOnlySpan(a); + + var values = new ReadOnlySpan(new byte[] { default(byte), 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + ReadOnlySpan span = new ReadOnlySpan(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + int expectedIndex = length / 2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + ReadOnlySpan span = new ReadOnlySpan(a); + + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = (byte)rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length * 2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchMany_Byte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + ReadOnlySpan span = new ReadOnlySpan(a); + var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeMany_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + ReadOnlySpan span = new ReadOnlySpan(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } } } diff --git a/src/System.Memory/tests/Span/IndexOf.byte.cs b/src/System.Memory/tests/Span/IndexOf.byte.cs index 0e555aaee797..f2e7b399f327 100644 --- a/src/System.Memory/tests/Span/IndexOf.byte.cs +++ b/src/System.Memory/tests/Span/IndexOf.byte.cs @@ -350,5 +350,196 @@ public static void MakeSureNoChecksGoOutOfRangeThree_Byte() Assert.Equal(-1, index); } } + + [Fact] + public static void ZeroLengthIndexOfMany_Byte() + { + Span sp = new Span(Array.Empty()); + var values = new ReadOnlySpan(new byte[] {0, 0, 0, 0}); + int idx = sp.IndexOfAny(values); + Assert.Equal(-1, idx); + + values = new ReadOnlySpan(new byte[] {}); + idx = sp.IndexOfAny(values); + Assert.Equal(0, idx); + } + + [Fact] + public static void DefaultFilledIndexOfMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + Span span = new Span(a); + + var values = new ReadOnlySpan(new byte[] { default(byte), 99, 98, 0 }); + + for (int i = 0; i < length; i++) + { + int idx = span.IndexOfAny(values); + Assert.Equal(0, idx); + } + } + } + + [Fact] + public static void TestMatchMany_Byte() + { + for (int length = 0; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + a[i] = (byte)(i + 1); + } + Span span = new Span(a); + + for (int targetIndex = 0; targetIndex < length; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], 0, 0, 0 }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { a[targetIndex], a[targetIndex + 1], a[targetIndex + 2], a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex, idx); + } + + for (int targetIndex = 0; targetIndex < length - 3; targetIndex++) + { + var values = new ReadOnlySpan(new byte[] { 0, 0, 0, a[targetIndex + 3] }); + int idx = span.IndexOfAny(values); + Assert.Equal(targetIndex + 3, idx); + } + } + } + + [Fact] + public static void TestMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 2; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + int expectedIndex = length/2; + for (int i = 0; i < length; i++) + { + if (i == expectedIndex) + { + continue; + } + a[i] = 255; + } + Span span = new Span(a); + + byte[] targets = new byte[length*2]; + for (int i = 0; i < targets.Length; i++) + { + if (i == length + 1) + { + continue; + } + targets[i] = (byte)rnd.Next(1, 255); + } + + var values = new ReadOnlySpan(targets); + int idx = span.IndexOfAny(values); + Assert.Equal(expectedIndex, idx); + } + } + + [Fact] + public static void TestNoMatchMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte) rnd.Next(1, 256); + } + Span span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestNoMatchValuesLargerMany_Byte() + { + var rnd = new Random(42); + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + byte[] targets = new byte[length*2]; + for (int i = 0; i < targets.Length; i++) + { + targets[i] = (byte)rnd.Next(1, 256); + } + Span span = new Span(a); + var values = new ReadOnlySpan(targets); + + int idx = span.IndexOfAny(values); + Assert.Equal(-1, idx); + } + } + + [Fact] + public static void TestMultipleMatchMany_Byte() + { + for (int length = 5; length < byte.MaxValue; length++) + { + byte[] a = new byte[length]; + for (int i = 0; i < length; i++) + { + byte val = (byte)(i + 1); + a[i] = val == 200 ? (byte)201 : val; + } + + a[length - 1] = 200; + a[length - 2] = 200; + a[length - 3] = 200; + a[length - 4] = 200; + a[length - 5] = 200; + + Span span = new Span(a); + var values = new ReadOnlySpan(new byte[] { 200, 200, 200, 200, 200, 200, 200, 200, 200 }); + int idx = span.IndexOfAny(values); + Assert.Equal(length - 5, idx); + } + } + + [Fact] + public static void MakeSureNoChecksGoOutOfRangeMany_Byte() + { + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 98; + Span span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 98, 99, 98, 99, 98 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + + for (int length = 1; length < byte.MaxValue; length++) + { + byte[] a = new byte[length + 2]; + a[0] = 99; + a[length + 1] = 99; + Span span = new Span(a, 1, length - 1); + var values = new ReadOnlySpan(new byte[] { 99, 99, 99, 99, 99, 99 }); + int index = span.IndexOfAny(values); + Assert.Equal(-1, index); + } + } } } From 600942e4949a8e5dc9e59c00c6c5ecb8baec33a6 Mon Sep 17 00:00:00 2001 From: ahsonkhan Date: Wed, 29 Mar 2017 19:00:30 -0700 Subject: [PATCH 6/6] Addressing PR comments. --- src/System.Memory/src/System/SpanHelpers.byte.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Memory/src/System/SpanHelpers.byte.cs b/src/System.Memory/src/System/SpanHelpers.byte.cs index 1cf13d9a2899..bc98d0b7ff33 100644 --- a/src/System.Memory/src/System/SpanHelpers.byte.cs +++ b/src/System.Memory/src/System/SpanHelpers.byte.cs @@ -210,7 +210,7 @@ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte valu unchecked { int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); - nLength = (IntPtr)(uint)unaligned; + nLength = (IntPtr)(uint)((Vector.Count - unaligned) & (Vector.Count - 1)); } } SequentialScan: @@ -285,7 +285,7 @@ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte valu { goto NotFound; } - nLength = (IntPtr)(uint)(length - Vector.Count); + nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); // Get comparision Vector Vector values0 = GetVector(value0); Vector values1 = GetVector(value1); @@ -359,7 +359,7 @@ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte valu unchecked { int unaligned = (int)(byte*)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); - nLength = (IntPtr)(uint)unaligned; + nLength = (IntPtr)(uint)((Vector.Count - unaligned) & (Vector.Count - 1)); } } SequentialScan: @@ -434,7 +434,7 @@ public static unsafe int IndexOfAny(ref byte searchSpace, byte value0, byte valu { goto NotFound; } - nLength = (IntPtr)(uint)(length - Vector.Count); + nLength = (IntPtr)(uint)((length - (uint)index) & ~(Vector.Count - 1)); // Get comparision Vector Vector values0 = GetVector(value0); Vector values1 = GetVector(value1);