From 1acd487d25dacb64bc7e6c2d83394ecff117b7d3 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Wed, 8 Jan 2020 14:58:36 -0800 Subject: [PATCH 1/2] Relax some asserts in SpanHelpers.T.cs --- .../src/System/MemoryExtensions.cs | 44 ++++++++++++++----- .../src/System/SpanHelpers.Char.cs | 34 ++++++++++++++ .../src/System/SpanHelpers.T.cs | 15 ++++--- 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 63534adc023104..fdc17764ac8b7c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -405,12 +405,22 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this Span span, ReadOnlySpan value) where T : IEquatable { - if (Unsafe.SizeOf() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable()) - return SpanHelpers.LastIndexOf( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - span.Length, - ref Unsafe.As(ref MemoryMarshal.GetReference(value)), - value.Length); + if (RuntimeHelpers.IsBitwiseEquatable()) + { + if (Unsafe.SizeOf() == sizeof(byte)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + + if (Unsafe.SizeOf() == sizeof(char)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + } return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } @@ -550,12 +560,22 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { - if (Unsafe.SizeOf() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable()) - return SpanHelpers.LastIndexOf( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - span.Length, - ref Unsafe.As(ref MemoryMarshal.GetReference(value)), - value.Length); + if (RuntimeHelpers.IsBitwiseEquatable()) + { + if (Unsafe.SizeOf() == sizeof(byte)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + + if (Unsafe.SizeOf() == sizeof(char)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As(ref MemoryMarshal.GetReference(value)), + value.Length); + } return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs index 25dc8f6fae4c6f..53ac7c78028447 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs @@ -871,6 +871,40 @@ public static unsafe int IndexOfAny(ref char searchSpace, char value0, char valu } } + public static int LastIndexOf(ref char searchSpace, int searchSpaceLength, ref char 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. + + char valueHead = value; + ref char valueTail = ref Unsafe.Add(ref value, 1); + int valueTailLength = valueLength - 1; + + int index = 0; + while (true) + { + Debug.Assert(0 <= index && index <= searchSpaceLength); // Ensures no deceptive underflows in the computation of "remainingSearchSpaceLength". + int remainingSearchSpaceLength = searchSpaceLength - index - valueTailLength; + if (remainingSearchSpaceLength <= 0) + break; // The unsearched portion is now shorter than the sequence we're looking for. So it can't be there. + + // Do a quick search for the first element of "value". + int relativeIndex = LastIndexOf(ref searchSpace, valueHead, remainingSearchSpaceLength); + if (relativeIndex == -1) + break; + + // Found the first element of "value". See if the tail matches. + if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength)) + return relativeIndex; // The tail matched. Return a successful find. + + index += remainingSearchSpaceLength - relativeIndex; + } + return -1; + } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public static unsafe int LastIndexOf(ref char searchSpace, char value, int length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs index 87918217de1db6..98cd686bd6223a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs @@ -456,6 +456,9 @@ public static int IndexOfAny(ref T searchSpace, int searchSpaceLength, ref T public static int LastIndexOf(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { + // The optimized implementation should be used for these types + Debug.Assert(!RuntimeHelpers.IsBitwiseEquatable() || !(Unsafe.SizeOf() == sizeof(byte) || Unsafe.SizeOf() == sizeof(char))); + Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); @@ -574,8 +577,8 @@ public static int LastIndexOf(ref T searchSpace, T value, int length) where T public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, int length) where T : IEquatable { - // The optimized implementation should be used for these types - Debug.Assert(!RuntimeHelpers.IsBitwiseEquatable() || !(Unsafe.SizeOf() == sizeof(byte) || Unsafe.SizeOf() == sizeof(char))); + // The optimized implementation should be used for byte + Debug.Assert(!RuntimeHelpers.IsBitwiseEquatable() || Unsafe.SizeOf() != sizeof(byte)); Debug.Assert(length >= 0); @@ -680,8 +683,8 @@ public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, int l public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, T value2, int length) where T : IEquatable { - // The optimized implementation should be used for these types - Debug.Assert(!RuntimeHelpers.IsBitwiseEquatable() || !(Unsafe.SizeOf() == sizeof(byte) || Unsafe.SizeOf() == sizeof(char))); + // The optimized implementation should be used for byte + Debug.Assert(!RuntimeHelpers.IsBitwiseEquatable() || Unsafe.SizeOf() != sizeof(byte)); Debug.Assert(length >= 0); @@ -786,8 +789,8 @@ public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, T val public static int LastIndexOfAny(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { - // The optimized implementation should be used for these types - Debug.Assert(!RuntimeHelpers.IsBitwiseEquatable() || !(Unsafe.SizeOf() == sizeof(byte) || Unsafe.SizeOf() == sizeof(char))); + // The optimized implementation should be used for byte + Debug.Assert(!RuntimeHelpers.IsBitwiseEquatable() || Unsafe.SizeOf() != sizeof(byte)); Debug.Assert(searchSpaceLength >= 0); Debug.Assert(valueLength >= 0); From 39b8dfea2bdd4bd981422591ce7a69c649117dec Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 9 Jan 2020 15:24:04 -0800 Subject: [PATCH 2/2] Call byte SequenceEqual --- .../System.Private.CoreLib/src/System/SpanHelpers.Char.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs index 53ac7c78028447..4f0f2d32ff596a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs @@ -897,8 +897,13 @@ public static int LastIndexOf(ref char searchSpace, int searchSpaceLength, ref c break; // Found the first element of "value". See if the tail matches. - if (SequenceEqual(ref Unsafe.Add(ref searchSpace, relativeIndex + 1), ref valueTail, valueTailLength)) + if (SequenceEqual( + ref Unsafe.As(ref Unsafe.Add(ref searchSpace, relativeIndex + 1)), + ref Unsafe.As(ref valueTail), + (nuint)valueTailLength * 2)) + { return relativeIndex; // The tail matched. Return a successful find. + } index += remainingSearchSpaceLength - relativeIndex; }