-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Convert Valuetype.FastEqualsCheck and GetHashCodeOfPtr to managed #69723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b8a4c9f
6cdc78b
a90b2d9
c5fa630
2234639
3b9f848
a040d8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,9 +10,11 @@ | |
| ** | ||
| ===========================================================*/ | ||
|
|
||
| using System.Diagnostics; | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Reflection; | ||
| using System.Runtime.CompilerServices; | ||
| using System.Threading; | ||
|
|
||
| namespace System | ||
| { | ||
|
|
@@ -22,34 +24,34 @@ public abstract class ValueType | |
| { | ||
| [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", | ||
| Justification = "Trimmed fields don't make a difference for equality")] | ||
| public override bool Equals([NotNullWhen(true)] object? obj) | ||
| public unsafe override bool Equals([NotNullWhen(true)] object? obj) | ||
| { | ||
| if (null == obj) | ||
| { | ||
| return false; | ||
| } | ||
| Type thisType = this.GetType(); | ||
| Type thatType = obj.GetType(); | ||
|
|
||
| if (thatType != thisType) | ||
| if (GetType() != obj.GetType()) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| object thisObj = (object)this; | ||
| object? thisResult, thatResult; | ||
|
|
||
| // if there are no GC references in this object we can avoid reflection | ||
| // and do a fast memcmp | ||
| if (CanCompareBits(this)) | ||
| return FastEqualsCheck(thisObj, obj); | ||
| { | ||
| return SpanHelpers.SequenceEqual( | ||
| ref RuntimeHelpers.GetRawData(this), | ||
| ref RuntimeHelpers.GetRawData(obj), | ||
| RuntimeHelpers.GetMethodTable(this)->GetNumInstanceFieldBytes()); | ||
| } | ||
|
|
||
| FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | ||
| FieldInfo[] thisFields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | ||
|
|
||
| for (int i = 0; i < thisFields.Length; i++) | ||
| { | ||
| thisResult = thisFields[i].GetValue(thisObj); | ||
| thatResult = thisFields[i].GetValue(obj); | ||
| object? thisResult = thisFields[i].GetValue(this); | ||
| object? thatResult = thisFields[i].GetValue(obj); | ||
|
|
||
| if (thisResult == null) | ||
| { | ||
|
|
@@ -69,9 +71,6 @@ public override bool Equals([NotNullWhen(true)] object? obj) | |
| [MethodImpl(MethodImplOptions.InternalCall)] | ||
| private static extern bool CanCompareBits(object obj); | ||
|
|
||
| [MethodImpl(MethodImplOptions.InternalCall)] | ||
| private static extern bool FastEqualsCheck(object a, object b); | ||
|
|
||
| /*=================================GetHashCode================================== | ||
| **Action: Our algorithm for returning the hashcode is a little bit complex. We look | ||
| ** for the first non-static field and get its hashcode. If the type has no | ||
|
|
@@ -85,8 +84,32 @@ public override bool Equals([NotNullWhen(true)] object? obj) | |
| [MethodImpl(MethodImplOptions.InternalCall)] | ||
| public extern override int GetHashCode(); | ||
|
|
||
| [MethodImpl(MethodImplOptions.InternalCall)] | ||
| internal static extern int GetHashCodeOfPtr(IntPtr ptr); | ||
| private static int s_seed; | ||
|
|
||
| internal static int GetHashCodeOfPtr(IntPtr ptr) | ||
| { | ||
| int hashCode = (int)ptr; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is dropping the upper bits of a 64 bit pointer. Is this wise? All pointers offset by 4GB will result in the same hash code.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is for runtime handles which are pointers to unmanaged tables. I don't expect they would span across 4GB. |
||
|
|
||
| if (hashCode == 0) | ||
| { | ||
| return 0; | ||
| } | ||
|
|
||
| int seed = s_seed; | ||
|
|
||
| // Initialize s_seed lazily | ||
| if (seed == 0) | ||
| { | ||
| // We use the first non-0 pointer as the seed, all hashcodes will be based off that. | ||
| // This is to make sure that we only reveal relative memory addresses and never absolute ones. | ||
| seed = hashCode; | ||
| Interlocked.CompareExchange(ref s_seed, seed, 0); | ||
| seed = s_seed; | ||
| } | ||
|
|
||
| Debug.Assert(s_seed != 0); | ||
| return hashCode - seed; | ||
| } | ||
|
|
||
| public override string? ToString() | ||
| { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.