1
1
// Copyright The OpenTelemetry Authors
2
2
// SPDX-License-Identifier: Apache-2.0
3
3
4
+ using System . Diagnostics ;
5
+ using System . Runtime . CompilerServices ;
6
+ #if NET6_0_OR_GREATER
7
+ using System . Runtime . InteropServices ;
8
+ #endif
9
+
4
10
namespace OpenTelemetry . Metrics ;
5
11
6
12
internal readonly struct Tags : IEquatable < Tags >
@@ -12,31 +18,7 @@ namespace OpenTelemetry.Metrics;
12
18
public Tags ( KeyValuePair < string , object ? > [ ] keyValuePairs )
13
19
{
14
20
this . KeyValuePairs = keyValuePairs ;
15
-
16
- #if NET6_0_OR_GREATER
17
- HashCode hashCode = default ;
18
- for ( int i = 0 ; i < this . KeyValuePairs . Length ; i ++ )
19
- {
20
- ref var item = ref this . KeyValuePairs [ i ] ;
21
- hashCode . Add ( item . Key ) ;
22
- hashCode . Add ( item . Value ) ;
23
- }
24
-
25
- var hash = hashCode . ToHashCode ( ) ;
26
- #else
27
- var hash = 17 ;
28
- for ( int i = 0 ; i < this . KeyValuePairs . Length ; i ++ )
29
- {
30
- ref var item = ref this . KeyValuePairs [ i ] ;
31
- unchecked
32
- {
33
- hash = ( hash * 31 ) + ( item . Key ? . GetHashCode ( ) ?? 0 ) ;
34
- hash = ( hash * 31 ) + ( item . Value ? . GetHashCode ( ) ?? 0 ) ;
35
- }
36
- }
37
- #endif
38
-
39
- this . hashCode = hash ;
21
+ this . hashCode = ComputeHashCode ( keyValuePairs ) ;
40
22
}
41
23
42
24
public readonly KeyValuePair < string , object ? > [ ] KeyValuePairs { get ; }
@@ -52,33 +34,101 @@ public override readonly bool Equals(object? obj)
52
34
53
35
public readonly bool Equals ( Tags other )
54
36
{
55
- var length = this . KeyValuePairs . Length ;
37
+ var ourKvps = this . KeyValuePairs ;
38
+ var theirKvps = other . KeyValuePairs ;
56
39
57
- if ( length != other . KeyValuePairs . Length )
40
+ var length = ourKvps . Length ;
41
+
42
+ if ( length != theirKvps . Length )
58
43
{
59
44
return false ;
60
45
}
61
46
47
+ #if NET6_0_OR_GREATER
48
+ // Note: This loop uses unsafe code (pointers) to elide bounds checks on
49
+ // two arrays we know to be of equal length.
50
+ if ( length > 0 )
51
+ {
52
+ ref var ours = ref MemoryMarshal . GetArrayDataReference ( ourKvps ) ;
53
+ ref var theirs = ref MemoryMarshal . GetArrayDataReference ( theirKvps ) ;
54
+ while ( true )
55
+ {
56
+ // Note: string.Equals performs an ordinal comparison
57
+ if ( ! ours . Key . Equals ( theirs . Key ) )
58
+ {
59
+ return false ;
60
+ }
61
+
62
+ if ( ! ours . Value ? . Equals ( theirs . Value ) ?? theirs . Value != null )
63
+ {
64
+ return false ;
65
+ }
66
+
67
+ if ( -- length == 0 )
68
+ {
69
+ break ;
70
+ }
71
+
72
+ ours = ref Unsafe . Add ( ref ours , 1 ) ;
73
+ theirs = ref Unsafe . Add ( ref theirs , 1 ) ;
74
+ }
75
+ }
76
+ #else
62
77
for ( int i = 0 ; i < length ; i ++ )
63
78
{
64
- ref var left = ref this . KeyValuePairs [ i ] ;
65
- ref var right = ref other . KeyValuePairs [ i ] ;
79
+ ref var ours = ref ourKvps [ i ] ;
80
+
81
+ // Note: Bounds check happens here for theirKvps element access
82
+ ref var theirs = ref theirKvps [ i ] ;
66
83
67
- // Equality check for Keys
68
- if ( ! left . Key . Equals ( right . Key , StringComparison . Ordinal ) )
84
+ // Note: string.Equals performs an ordinal comparison
85
+ if ( ! ours . Key . Equals ( theirs . Key ) )
69
86
{
70
87
return false ;
71
88
}
72
89
73
- // Equality check for Values
74
- if ( ! left . Value ? . Equals ( right . Value ) ?? right . Value != null )
90
+ if ( ! ours . Value ? . Equals ( theirs . Value ) ?? theirs . Value != null )
75
91
{
76
92
return false ;
77
93
}
78
94
}
95
+ #endif
79
96
80
97
return true ;
81
98
}
82
99
83
100
public override readonly int GetHashCode ( ) => this . hashCode ;
101
+
102
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
103
+ private static int ComputeHashCode ( KeyValuePair < string , object ? > [ ] keyValuePairs )
104
+ {
105
+ Debug . Assert ( keyValuePairs != null , "keyValuePairs was null" ) ;
106
+
107
+ #if NET6_0_OR_GREATER
108
+ HashCode hashCode = default ;
109
+
110
+ for ( int i = 0 ; i < keyValuePairs . Length ; i ++ )
111
+ {
112
+ ref var item = ref keyValuePairs [ i ] ;
113
+ hashCode . Add ( item . Key . GetHashCode ( ) ) ;
114
+ hashCode . Add ( item . Value ) ;
115
+ }
116
+
117
+ return hashCode . ToHashCode ( ) ;
118
+ #else
119
+ var hash = 17 ;
120
+
121
+ for ( int i = 0 ; i < keyValuePairs ! . Length ; i ++ )
122
+ {
123
+ ref var item = ref keyValuePairs [ i ] ;
124
+ unchecked
125
+ {
126
+ hash = ( hash * 31 ) + item . Key . GetHashCode ( ) ;
127
+ hash = ( hash * 31 ) + ( item . Value ? . GetHashCode ( ) ?? 0 ) ;
128
+ }
129
+ }
130
+
131
+ return hash ;
132
+ #endif
133
+ }
84
134
}
0 commit comments