Repository navigation
Expand file tree
/
Copy pathValueType.cs
More file actions
173 lines (148 loc) · 7.2 KB
/
Copy pathValueType.cs
File metadata and controls
173 lines (148 loc) · 7.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
/*============================================================
**
**
**
** Purpose: Base class for all value classes.
**
**
===========================================================*/
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System
{
[Serializable]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public abstract partial class ValueType
{
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
Justification = "Trimmed fields don't make a difference for equality")]
public override unsafe bool Equals([NotNullWhen(true)] object? obj)
{
if (null == obj)
{
return false;
}
if (GetType() != obj.GetType())
{
return false;
}
// if there are no GC references in this object we can avoid reflection
// and do a fast memcmp
if (CanCompareBitsOrUseFastGetHashCode(RuntimeHelpers.GetMethodTable(obj))) // MethodTable kept alive by access to object below
{
return SpanHelpers.SequenceEqual(
ref RuntimeHelpers.GetRawData(this),
ref RuntimeHelpers.GetRawData(obj),
RuntimeHelpers.GetMethodTable(this)->GetNumInstanceFieldBytes());
}
FieldInfo[] thisFields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i = 0; i < thisFields.Length; i++)
{
object? thisResult = thisFields[i].GetValue(this);
object? thatResult = thisFields[i].GetValue(obj);
if (thisResult == null)
{
if (thatResult != null)
return false;
}
else
if (!thisResult.Equals(thatResult))
{
return false;
}
}
return true;
}
// Return true if the valuetype does not contain pointer, is tightly packed,
// does not have floating point number field and does not override Equals method.
private static unsafe bool CanCompareBitsOrUseFastGetHashCode(MethodTable* pMT)
{
MethodTableAuxiliaryData* pAuxData = pMT->AuxiliaryData;
if (pAuxData->HasCheckedCanCompareBitsOrUseFastGetHashCode)
{
return pAuxData->CanCompareBitsOrUseFastGetHashCode;
}
return CanCompareBitsOrUseFastGetHashCodeHelper(pMT);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MethodTable_CanCompareBitsOrUseFastGetHashCode")]
[return: MarshalAs(UnmanagedType.Bool)]
private static unsafe partial bool CanCompareBitsOrUseFastGetHashCodeHelper(MethodTable* pMT);
/*=================================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
** non-static fields, we return the hashcode of the type. We can't take the
** hashcode of a static member because if that member is of the same type as
** the original type, we'll end up in an infinite loop.
**Returns: The hashcode for the type.
**Arguments: None.
**Exceptions: None.
==============================================================================*/
public override unsafe int GetHashCode()
{
// The default implementation of GetHashCode() for all value types.
// Note that this implementation reveals the value of the fields.
// So if the value type contains any sensitive information it should
// implement its own GetHashCode().
MethodTable* pMT = RuntimeHelpers.GetMethodTable(this);
ref byte rawData = ref RuntimeHelpers.GetRawData(this);
HashCode hashCode = default;
// To get less colliding and more evenly distributed hash codes,
// we munge the class index into the hashcode
hashCode.Add((IntPtr)pMT);
if (CanCompareBitsOrUseFastGetHashCode(pMT))
{
// this is a struct with no refs and no "strange" offsets
uint size = pMT->GetNumInstanceFieldBytes();
hashCode.AddBytes(MemoryMarshal.CreateReadOnlySpan(ref rawData, (int)size));
}
else
{
object thisRef = this;
switch (GetHashCodeStrategy(pMT, ObjectHandleOnStack.Create(ref thisRef), out uint fieldOffset, out uint fieldSize, out MethodTable* fieldMT))
{
case ValueTypeHashCodeStrategy.ReferenceField:
hashCode.Add(Unsafe.As<byte, object>(ref Unsafe.AddByteOffset(ref rawData, fieldOffset)).GetHashCode());
break;
case ValueTypeHashCodeStrategy.DoubleField:
hashCode.Add(Unsafe.As<byte, double>(ref Unsafe.AddByteOffset(ref rawData, fieldOffset)).GetHashCode());
break;
case ValueTypeHashCodeStrategy.SingleField:
hashCode.Add(Unsafe.As<byte, float>(ref Unsafe.AddByteOffset(ref rawData, fieldOffset)).GetHashCode());
break;
case ValueTypeHashCodeStrategy.FastGetHashCode:
Debug.Assert(fieldSize != 0);
hashCode.AddBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AddByteOffset(ref rawData, fieldOffset), (int)fieldSize));
break;
case ValueTypeHashCodeStrategy.ValueTypeOverride:
Debug.Assert(fieldMT != null);
// Box the field to handle complicated cases like mutable method and shared generic
hashCode.Add(RuntimeHelpers.Box(fieldMT, ref Unsafe.AddByteOffset(ref rawData, fieldOffset))?.GetHashCode() ?? 0);
break;
}
}
return hashCode.ToHashCode();
}
// Must match the definition in src\vm\comutilnative.cpp
private enum ValueTypeHashCodeStrategy
{
None,
ReferenceField,
DoubleField,
SingleField,
FastGetHashCode,
ValueTypeOverride,
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ValueType_GetHashCodeStrategy")]
private static unsafe partial ValueTypeHashCodeStrategy GetHashCodeStrategy(
MethodTable* pMT, ObjectHandleOnStack objHandle, out uint fieldOffset, out uint fieldSize, out MethodTable* fieldMT);
public override string? ToString()
{
return this.GetType().ToString();
}
}
}