11// Copyright The OpenTelemetry Authors
22// SPDX-License-Identifier: Apache-2.0
33
4+ using System . Diagnostics ;
5+ using System . Runtime . CompilerServices ;
6+ #if NET6_0_OR_GREATER
7+ using System . Runtime . InteropServices ;
8+ #endif
9+
410namespace OpenTelemetry . Metrics ;
511
612internal readonly struct Tags : IEquatable < Tags >
@@ -12,31 +18,7 @@ namespace OpenTelemetry.Metrics;
1218 public Tags ( KeyValuePair < string , object ? > [ ] keyValuePairs )
1319 {
1420 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 ) ;
4022 }
4123
4224 public readonly KeyValuePair < string , object ? > [ ] KeyValuePairs { get ; }
@@ -52,33 +34,101 @@ public override readonly bool Equals(object? obj)
5234
5335 public readonly bool Equals ( Tags other )
5436 {
55- var length = this . KeyValuePairs . Length ;
37+ var ourKvps = this . KeyValuePairs ;
38+ var theirKvps = other . KeyValuePairs ;
5639
57- if ( length != other . KeyValuePairs . Length )
40+ var length = ourKvps . Length ;
41+
42+ if ( length != theirKvps . Length )
5843 {
5944 return false ;
6045 }
6146
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
6277 for ( int i = 0 ; i < length ; i ++ )
6378 {
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 ] ;
6683
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 ) )
6986 {
7087 return false ;
7188 }
7289
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 )
7591 {
7692 return false ;
7793 }
7894 }
95+ #endif
7996
8097 return true ;
8198 }
8299
83100 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+ }
84134}
0 commit comments