|
5 | 5 | using System.Diagnostics;
|
6 | 6 | using System.Numerics;
|
7 | 7 | using System.Runtime.CompilerServices;
|
8 |
| -using System.Runtime.InteropServices; |
9 | 8 | using System.Runtime.Intrinsics;
|
10 | 9 |
|
11 | 10 | #pragma warning disable 8500 // sizeof of managed types
|
@@ -225,7 +224,7 @@ public static int IndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T val
|
225 | 224 | }
|
226 | 225 |
|
227 | 226 | // Adapted from IndexOf(...)
|
228 |
| - public static unsafe bool Contains<T>(ref T searchSpace, T value, int length) where T : IEquatable<T>? |
| 227 | + public static bool Contains<T>(ref T searchSpace, T value, int length) where T : IEquatable<T>? |
229 | 228 | {
|
230 | 229 | Debug.Assert(length >= 0);
|
231 | 230 |
|
@@ -297,7 +296,7 @@ public static unsafe bool Contains<T>(ref T searchSpace, T value, int length) wh
|
297 | 296 | return true;
|
298 | 297 | }
|
299 | 298 |
|
300 |
| - public static unsafe int IndexOf<T>(ref T searchSpace, T value, int length) where T : IEquatable<T>? |
| 299 | + public static int IndexOf<T>(ref T searchSpace, T value, int length) where T : IEquatable<T>? |
301 | 300 | {
|
302 | 301 | Debug.Assert(length >= 0);
|
303 | 302 |
|
@@ -1304,11 +1303,11 @@ public static int SequenceCompareTo<T>(ref T first, int firstLength, ref T secon
|
1304 | 1303 | }
|
1305 | 1304 |
|
1306 | 1305 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
1307 |
| - internal static unsafe bool ContainsValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumber<T> |
| 1306 | + internal static bool ContainsValueType<T>(ref T searchSpace, T value, int length) where T : struct, INumber<T> |
1308 | 1307 | {
|
1309 | 1308 | if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value))
|
1310 | 1309 | {
|
1311 |
| - return PackedSpanHelpers.Contains(ref Unsafe.As<T, short>(ref searchSpace), *(short*)&value, length); |
| 1310 | + return PackedSpanHelpers.Contains(ref Unsafe.As<T, short>(ref searchSpace), Unsafe.BitCast<T, short>(value), length); |
1312 | 1311 | }
|
1313 | 1312 |
|
1314 | 1313 | return NonPackedContainsValueType(ref searchSpace, value, length);
|
@@ -1478,15 +1477,15 @@ internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value, int
|
1478 | 1477 | => IndexOfValueType<T, Negate<T>>(ref searchSpace, value, length);
|
1479 | 1478 |
|
1480 | 1479 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
1481 |
| - private static unsafe int IndexOfValueType<TValue, TNegator>(ref TValue searchSpace, TValue value, int length) |
| 1480 | + private static int IndexOfValueType<TValue, TNegator>(ref TValue searchSpace, TValue value, int length) |
1482 | 1481 | where TValue : struct, INumber<TValue>
|
1483 | 1482 | where TNegator : struct, INegator<TValue>
|
1484 | 1483 | {
|
1485 | 1484 | if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value))
|
1486 | 1485 | {
|
1487 | 1486 | return typeof(TNegator) == typeof(DontNegate<short>)
|
1488 |
| - ? PackedSpanHelpers.IndexOf(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value, length) |
1489 |
| - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value, length); |
| 1487 | + ? PackedSpanHelpers.IndexOf(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TValue, char>(value), length) |
| 1488 | + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TValue, char>(value), length); |
1490 | 1489 | }
|
1491 | 1490 |
|
1492 | 1491 | return NonPackedIndexOfValueType<TValue, TNegator>(ref searchSpace, value, length);
|
@@ -1665,24 +1664,33 @@ internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T
|
1665 | 1664 | => IndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, length);
|
1666 | 1665 |
|
1667 | 1666 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
1668 |
| - private static unsafe int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, int length) |
| 1667 | + private static int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, int length) |
1669 | 1668 | where TValue : struct, INumber<TValue>
|
1670 | 1669 | where TNegator : struct, INegator<TValue>
|
1671 | 1670 | {
|
1672 | 1671 | if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1))
|
1673 | 1672 | {
|
1674 |
| - if ((*(char*)&value0 ^ *(char*)&value1) == 0x20) |
| 1673 | + char char0 = Unsafe.BitCast<TValue, char>(value0); |
| 1674 | + char char1 = Unsafe.BitCast<TValue, char>(value1); |
| 1675 | + |
| 1676 | + if (RuntimeHelpers.IsKnownConstant(value0) && RuntimeHelpers.IsKnownConstant(value1)) |
1675 | 1677 | {
|
1676 |
| - char lowerCase = (char)Math.Max(*(char*)&value0, *(char*)&value1); |
| 1678 | + // If the values differ only in the 0x20 bit, we can optimize the search by reducing the number of comparisons. |
| 1679 | + // This optimization only applies to a small subset of values and the throughput difference is not too significant. |
| 1680 | + // We avoid introducing per-call overhead for non-constant values by guarding this optimization behind RuntimeHelpers.IsKnownConstant. |
| 1681 | + if ((char0 ^ char1) == 0x20) |
| 1682 | + { |
| 1683 | + char lowerCase = (char)Math.Max(char0, char1); |
1677 | 1684 |
|
1678 |
| - return typeof(TNegator) == typeof(DontNegate<short>) |
1679 |
| - ? PackedSpanHelpers.IndexOfAnyIgnoreCase(ref Unsafe.As<TValue, char>(ref searchSpace), lowerCase, length) |
1680 |
| - : PackedSpanHelpers.IndexOfAnyExceptIgnoreCase(ref Unsafe.As<TValue, char>(ref searchSpace), lowerCase, length); |
| 1685 | + return typeof(TNegator) == typeof(DontNegate<short>) |
| 1686 | + ? PackedSpanHelpers.IndexOfAnyIgnoreCase(ref Unsafe.As<TValue, char>(ref searchSpace), lowerCase, length) |
| 1687 | + : PackedSpanHelpers.IndexOfAnyExceptIgnoreCase(ref Unsafe.As<TValue, char>(ref searchSpace), lowerCase, length); |
| 1688 | + } |
1681 | 1689 | }
|
1682 | 1690 |
|
1683 | 1691 | return typeof(TNegator) == typeof(DontNegate<short>)
|
1684 |
| - ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value0, *(char*)&value1, length) |
1685 |
| - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value0, *(char*)&value1, length); |
| 1692 | + ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), char0, char1, length) |
| 1693 | + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), char0, char1, length); |
1686 | 1694 | }
|
1687 | 1695 |
|
1688 | 1696 | return NonPackedIndexOfAnyValueType<TValue, TNegator>(ref searchSpace, value0, value1, length);
|
@@ -1882,15 +1890,15 @@ internal static int IndexOfAnyExceptValueType<T>(ref T searchSpace, T value0, T
|
1882 | 1890 | => IndexOfAnyValueType<T, Negate<T>>(ref searchSpace, value0, value1, value2, length);
|
1883 | 1891 |
|
1884 | 1892 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
1885 |
| - private static unsafe int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, TValue value2, int length) |
| 1893 | + private static int IndexOfAnyValueType<TValue, TNegator>(ref TValue searchSpace, TValue value0, TValue value1, TValue value2, int length) |
1886 | 1894 | where TValue : struct, INumber<TValue>
|
1887 | 1895 | where TNegator : struct, INegator<TValue>
|
1888 | 1896 | {
|
1889 | 1897 | if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1) && PackedSpanHelpers.CanUsePackedIndexOf(value2))
|
1890 | 1898 | {
|
1891 | 1899 | return typeof(TNegator) == typeof(DontNegate<short>)
|
1892 |
| - ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value0, *(char*)&value1, *(char*)&value2, length) |
1893 |
| - : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), *(char*)&value0, *(char*)&value1, *(char*)&value2, length); |
| 1900 | + ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TValue, char>(value0), Unsafe.BitCast<TValue, char>(value1), Unsafe.BitCast<TValue, char>(value2), length) |
| 1901 | + : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As<TValue, char>(ref searchSpace), Unsafe.BitCast<TValue, char>(value0), Unsafe.BitCast<TValue, char>(value1), Unsafe.BitCast<TValue, char>(value2), length); |
1894 | 1902 | }
|
1895 | 1903 |
|
1896 | 1904 | return NonPackedIndexOfAnyValueType<TValue, TNegator>(ref searchSpace, value0, value1, value2, length);
|
@@ -3466,15 +3474,15 @@ internal static int IndexOfAnyExceptInRangeUnsignedNumber<T>(ref T searchSpace,
|
3466 | 3474 | IndexOfAnyInRangeUnsignedNumber<T, Negate<T>>(ref searchSpace, lowInclusive, highInclusive, length);
|
3467 | 3475 |
|
3468 | 3476 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
3469 |
| - private static unsafe int IndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T searchSpace, T lowInclusive, T highInclusive, int length) |
| 3477 | + private static int IndexOfAnyInRangeUnsignedNumber<T, TNegator>(ref T searchSpace, T lowInclusive, T highInclusive, int length) |
3470 | 3478 | where T : struct, IUnsignedNumber<T>, IComparisonOperators<T, T, bool>
|
3471 | 3479 | where TNegator : struct, INegator<T>
|
3472 | 3480 | {
|
3473 | 3481 | if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(T) == typeof(ushort) && PackedSpanHelpers.CanUsePackedIndexOf(lowInclusive) && PackedSpanHelpers.CanUsePackedIndexOf(highInclusive) && highInclusive >= lowInclusive)
|
3474 | 3482 | {
|
3475 | 3483 | ref char charSearchSpace = ref Unsafe.As<T, char>(ref searchSpace);
|
3476 |
| - char charLowInclusive = *(char*)&lowInclusive; |
3477 |
| - char charRange = (char)(*(char*)&highInclusive - charLowInclusive); |
| 3484 | + char charLowInclusive = Unsafe.BitCast<T, char>(lowInclusive); |
| 3485 | + char charRange = (char)(Unsafe.BitCast<T, char>(highInclusive) - charLowInclusive); |
3478 | 3486 |
|
3479 | 3487 | return typeof(TNegator) == typeof(DontNegate<ushort>)
|
3480 | 3488 | ? PackedSpanHelpers.IndexOfAnyInRange(ref charSearchSpace, charLowInclusive, charRange, length)
|
|
0 commit comments