MemoryExtensions.Contains (dotnet/coreclr#19863)
authorGrant <grant@jesanna.com>
Fri, 7 Sep 2018 22:25:42 +0000 (15:25 -0700)
committerJan Kotas <jkotas@microsoft.com>
Fri, 7 Sep 2018 22:25:42 +0000 (15:25 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/91820bdd42711266faae1c15d2176b9f47e48efe

src/libraries/System.Private.CoreLib/src/System/Guid.cs
src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.Fast.cs
src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs
src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Byte.cs
src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Char.cs
src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs
src/libraries/System.Private.CoreLib/src/System/Version.cs

index 01c0e883f88c483ee4a2f06659e94cb777625405..c57d3c4d9e86eff00199f35ba5df330cd9c237f1 100644 (file)
@@ -427,7 +427,7 @@ namespace System
             }
 
             // Check for dashes
-            bool dashesExistInString = guidString.IndexOf('-') >= 0;
+            bool dashesExistInString = guidString.Contains('-');
 
             if (dashesExistInString)
             {
index efd11c9c54cf69a302e0f35cb69839394b01ce8c..e86ca4fcd6e8be4b3e6c5049ea115ef37dbd1ae9 100644 (file)
@@ -83,20 +83,6 @@ namespace System
             return CompareInfo.EqualsOrdinalIgnoreCase(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), span.Length);
         }
 
-        // TODO https://github.com/dotnet/corefx/issues/27526
-        internal static bool Contains(this ReadOnlySpan<char> source, char value)
-        {
-            for (int i = 0; i < source.Length; i++)
-            {
-                if (source[i] == value)
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
         /// <summary>
         /// Compares the specified <paramref name="span"/> and <paramref name="other"/> using the specified <paramref name="comparisonType"/>,
         /// and returns an integer that indicates their relative position in the sort order.
index 739bc3145d1cc164ae75153adc2fd5fa175e4a8b..f0937c448b379abdb9085c811bd89d5dd968d6d3 100644 (file)
@@ -182,6 +182,56 @@ namespace System
             return true;
         }
 
+        /// <summary>
+        /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T).
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="span">The span to search.</param>
+        /// <param name="value">The value to search for.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool Contains<T>(this Span<T> span, T value)
+            where T : IEquatable<T>
+        {
+            if (typeof(T) == typeof(byte))
+                return SpanHelpers.Contains(
+                    ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+                    Unsafe.As<T, byte>(ref value),
+                    span.Length);
+
+            if (typeof(T) == typeof(char))
+                return SpanHelpers.Contains(
+                    ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+                    Unsafe.As<T, char>(ref value),
+                    span.Length);
+
+            return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
+        }
+
+        /// <summary>
+        /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T).
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="span">The span to search.</param>
+        /// <param name="value">The value to search for.</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool Contains<T>(this ReadOnlySpan<T> span, T value)
+            where T : IEquatable<T>
+        {
+            if (typeof(T) == typeof(byte))
+                return SpanHelpers.Contains(
+                    ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+                    Unsafe.As<T, byte>(ref value),
+                    span.Length);
+
+            if (typeof(T) == typeof(char))
+                return SpanHelpers.Contains(
+                    ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+                    Unsafe.As<T, char>(ref value),
+                    span.Length);
+
+            return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
+        }
+
         /// <summary>
         /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). 
         /// </summary>
@@ -196,6 +246,7 @@ namespace System
                     ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
                     Unsafe.As<T, byte>(ref value),
                     span.Length);
+
             if (typeof(T) == typeof(char))
                 return SpanHelpers.IndexOf(
                     ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
@@ -238,6 +289,7 @@ namespace System
                     ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
                     Unsafe.As<T, byte>(ref value),
                     span.Length);
+
             if (typeof(T) == typeof(char))
                 return SpanHelpers.LastIndexOf(
                     ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
@@ -322,6 +374,7 @@ namespace System
                     ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
                     Unsafe.As<T, byte>(ref value),
                     span.Length);
+
             if (typeof(T) == typeof(char))
                 return SpanHelpers.IndexOf(
                     ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
@@ -364,6 +417,7 @@ namespace System
                     ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
                     Unsafe.As<T, byte>(ref value),
                     span.Length);
+
             if (typeof(T) == typeof(char))
                 return SpanHelpers.LastIndexOf(
                     ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
index 2890adb568f402a117b4ab50182aac5a2968007d..373065363b246b360bfbea5f74dc97f4b38a74c8 100644 (file)
@@ -16,7 +16,7 @@ using nuint = System.UInt32;
 
 namespace System
 {
-    internal static partial class SpanHelpers
+    internal static partial class SpanHelpers // .Byte
     {
         public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength)
         {
@@ -96,6 +96,98 @@ namespace System
             return index;
         }
 
+        // Adapted from IndexOf(...)
+        public static unsafe bool Contains(ref byte searchSpace, byte value, int length)
+        {
+            Debug.Assert(length >= 0);
+            
+            uint uValue = value; // Use uint for comparisons to avoid unnecessary 8->32 extensions
+            IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+            IntPtr nLength = (IntPtr)length;
+
+            if (Vector.IsHardwareAccelerated && length >= Vector<byte>.Count * 2)
+            {
+                int unaligned = (int)Unsafe.AsPointer(ref searchSpace) & (Vector<byte>.Count - 1);
+                nLength = (IntPtr)((Vector<byte>.Count - unaligned) & (Vector<byte>.Count - 1));
+            }
+
+        SequentialScan:
+            while ((byte*)nLength >= (byte*)8)
+            {
+                nLength -= 8;
+
+                if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 0) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 4) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 5) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 6) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 7))
+                {
+                    goto Found;
+                }
+
+                index += 8;
+            }
+
+            if ((byte*)nLength >= (byte*)4)
+            {
+                nLength -= 4;
+
+                if (uValue == Unsafe.AddByteOffset(ref searchSpace, index + 0) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 1) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 2) ||
+                    uValue == Unsafe.AddByteOffset(ref searchSpace, index + 3))
+                {
+                    goto Found;
+                }
+
+                index += 4;
+            }
+
+            while ((byte*)nLength > (byte*)0)
+            {
+                nLength -= 1;
+
+                if (uValue == Unsafe.AddByteOffset(ref searchSpace, index))
+                    goto Found;
+
+                index += 1;
+            }
+
+            if (Vector.IsHardwareAccelerated && ((int)(byte*)index < length))
+            {
+                nLength = (IntPtr)((length - (int)(byte*)index) & ~(Vector<byte>.Count - 1));
+
+                // Get comparison Vector
+                Vector<byte> vComparison = new Vector<byte>(value);
+
+                while ((byte*)nLength > (byte*)index)
+                {
+                    var vMatches = Vector.Equals(vComparison, Unsafe.ReadUnaligned<Vector<byte>>(ref Unsafe.AddByteOffset(ref searchSpace, index)));
+                    if (Vector<byte>.Zero.Equals(vMatches))
+                    {
+                        index += Vector<byte>.Count;
+                        continue;
+                    }
+
+                    goto Found;
+                }
+
+                if ((int)(byte*)index < length)
+                {
+                    nLength = (IntPtr)(length - (int)(byte*)index);
+                    goto SequentialScan;
+                }
+            }
+
+            return false;
+
+        Found:
+            return true;
+        }
+
         public static unsafe int IndexOf(ref byte searchSpace, byte value, int length)
         {
             Debug.Assert(length >= 0);
index 51ace58a39d58b8da4bddfb33e047169b95e1780..02df69feb5cbb2466c8f227cc1e75a0a9938a05d 100644 (file)
@@ -4,18 +4,15 @@
 
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using System.Numerics;
 
 #if !netstandard
 using Internal.Runtime.CompilerServices;
 #endif
 
-#if !netstandard11
-using System.Numerics;
-#endif
-
 namespace System
 {
-    internal static partial class SpanHelpers
+    internal static partial class SpanHelpers // .Char
     {
         public static unsafe int SequenceCompareTo(ref char first, int firstLength, ref char second, int secondLength)
         {
@@ -32,7 +29,6 @@ namespace System
 
             if ((byte*)minLength >= (byte*)(sizeof(UIntPtr) / sizeof(char)))
             {
-#if !netstandard11
                 if (Vector.IsHardwareAccelerated && (byte*)minLength >= (byte*)Vector<ushort>.Count)
                 {
                     IntPtr nLength = minLength - Vector<ushort>.Count;
@@ -47,7 +43,6 @@ namespace System
                     }
                     while ((byte*)nLength >= (byte*)i);
                 }
-#endif
 
                 while ((byte*)minLength >= (byte*)(i + sizeof(UIntPtr) / sizeof(char)))
                 {
@@ -81,6 +76,94 @@ namespace System
             return lengthDelta;
         }
 
+        // Adapted from IndexOf(...)
+        public static unsafe bool Contains(ref char searchSpace, char value, int length)
+        {
+            Debug.Assert(length >= 0);
+
+            fixed (char* pChars = &searchSpace)
+            {
+                char* pCh = pChars;
+                char* pEndCh = pCh + length;
+
+                if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
+                {
+                    // Figure out how many characters to read sequentially until we are vector aligned
+                    // This is equivalent to:
+                    //         unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte
+                    //         length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count
+                    const int elementsPerByte = sizeof(ushort) / sizeof(byte);
+                    int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
+                    length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
+                }
+
+        SequentialScan:
+                while (length >= 4)
+                {
+                    length -= 4;
+
+                    if (value == *pCh ||
+                        value == *(pCh + 1) ||
+                        value == *(pCh + 2) ||
+                        value == *(pCh + 3))
+                    {
+                        goto Found;
+                    }
+
+                    pCh += 4;
+                }
+
+                while (length > 0)
+                {
+                    length -= 1;
+
+                    if (value == *pCh)
+                        goto Found;
+
+                    pCh += 1;
+                }
+
+                // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
+                // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
+                if (Vector.IsHardwareAccelerated && pCh < pEndCh)
+                {
+                    // Get the highest multiple of Vector<ushort>.Count that is within the search space.
+                    // That will be how many times we iterate in the loop below.
+                    // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count)
+                    length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1));
+
+                    // Get comparison Vector
+                    Vector<ushort> vComparison = new Vector<ushort>(value);
+
+                    while (length > 0)
+                    {
+                        // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned
+                        Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0);
+                        Vector<ushort> vMatches = Vector.Equals(vComparison, Unsafe.Read<Vector<ushort>>(pCh));
+                        if (Vector<ushort>.Zero.Equals(vMatches))
+                        {
+                            pCh += Vector<ushort>.Count;
+                            length -= Vector<ushort>.Count;
+                            continue;
+                        }
+
+                        goto Found;
+                    }
+
+                    if (pCh < pEndCh)
+                    {
+                        length = (int)(pEndCh - pCh);
+                        goto SequentialScan;
+                    }
+                }
+
+                return false;
+
+        Found:
+                return true;
+            }
+        }
+
         public static unsafe int IndexOf(ref char searchSpace, char value, int length)
         {
             Debug.Assert(length >= 0);
@@ -90,7 +173,6 @@ namespace System
                 char* pCh = pChars;
                 char* pEndCh = pCh + length;
 
-#if !netstandard11
                 if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
                 {
                     // Figure out how many characters to read sequentially until we are vector aligned
@@ -101,8 +183,8 @@ namespace System
                     int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
                     length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1);
                 }
+
             SequentialScan:
-#endif
                 while (length >= 4)
                 {
                     length -= 4;
@@ -128,7 +210,7 @@ namespace System
 
                     pCh += 1;
                 }
-#if !netstandard11
+
                 // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
                 // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
                 if (Vector.IsHardwareAccelerated && pCh < pEndCh)
@@ -162,7 +244,7 @@ namespace System
                         goto SequentialScan;
                     }
                 }
-#endif
+
                 return -1;
             Found3:
                 pCh++;
@@ -184,7 +266,6 @@ namespace System
                 char* pCh = pChars + length;
                 char* pEndCh = pChars;
 
-#if !netstandard11
                 if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2)
                 {
                     // Figure out how many characters to read sequentially from the end until we are vector aligned
@@ -192,8 +273,8 @@ namespace System
                     const int elementsPerByte = sizeof(ushort) / sizeof(byte);
                     length = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte;
                 }
+
             SequentialScan:
-#endif
                 while (length >= 4)
                 {
                     length -= 4;
@@ -217,7 +298,7 @@ namespace System
                     if (*pCh == value)
                         goto Found;
                 }
-#if !netstandard11
+
                 // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow
                 // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated.
                 if (Vector.IsHardwareAccelerated && pCh > pEndCh)
@@ -252,7 +333,7 @@ namespace System
                         goto SequentialScan;
                     }
                 }
-#endif
+
                 return -1;
             Found:
                 return (int)(pCh - pEndCh);
@@ -265,7 +346,6 @@ namespace System
             }
         }
 
-#if !netstandard11
         // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static int LocateFirstFoundChar(Vector<ushort> match)
@@ -336,6 +416,5 @@ namespace System
             }
             return index;
         }
-#endif
     }
 }
index d7a27fd4c97905188fb55df8cafcffc9051a6aed..20fdc616dc80a868a16e9ed52467f71a1cf9450f 100644 (file)
@@ -4,18 +4,15 @@
 
 using System.Diagnostics;
 using System.Runtime.CompilerServices; // Do not remove. This is necessary for netstandard, since this file is mirrored into corefx
+using System.Numerics;
 
 #if !netstandard
 using Internal.Runtime.CompilerServices;
 #endif
 
-#if !netstandard11
-using System.Numerics;
-#endif
-
 namespace System
 {
-    internal static partial class SpanHelpers
+    internal static partial class SpanHelpers // .T
     {
         public static int IndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
             where T : IEquatable<T>
@@ -53,6 +50,63 @@ namespace System
             return -1;
         }
 
+        // Adapted from IndexOf(...)
+        public static unsafe bool Contains<T>(ref T searchSpace, T value, int length)
+               where T : IEquatable<T>
+        {
+            Debug.Assert(length >= 0);
+
+            IntPtr index = (IntPtr)0; // Use IntPtr for arithmetic to avoid unnecessary 64->32->64 truncations
+            while (length >= 8)
+            {
+                length -= 8;
+
+                if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 3)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 4)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 5)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 6)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
+                {
+                    goto Found;
+                }
+
+                index += 8;
+            }
+
+            if (length >= 4)
+            {
+                length -= 4;
+
+                if (value.Equals(Unsafe.Add(ref searchSpace, index + 0)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 1)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 2)) ||
+                    value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
+                {
+                    goto Found;
+                }
+
+                index += 4;
+            }
+
+            while (length > 0)
+            {
+                length -= 1;
+
+                if (value.Equals(Unsafe.Add(ref searchSpace, index)))
+                    goto Found;
+
+                index += 1;
+            }
+
+            return false;
+
+        Found:
+            return true;
+        }
+
         public static unsafe int IndexOf<T>(ref T searchSpace, T value, int length)
             where T : IEquatable<T>
         {
index 9d24270eb3aba41eef04fcd3234558a406cf7ab6..444ee488583c5bf70621c5de5955a0fd5fca0966 100644 (file)
@@ -341,7 +341,7 @@ namespace System
                 if (buildEnd != -1)
                 {
                     buildEnd += (minorEnd + 1);
-                    if (input.Slice(buildEnd + 1).IndexOf('.') != -1)
+                    if (input.Slice(buildEnd + 1).Contains('.'))
                     {
                         if (throwOnFailure) throw new ArgumentException(SR.Arg_VersionString, nameof(input));
                         return null;