33// See the LICENSE file in the project root for more information.
44
55using System ;
6+ using System . Diagnostics . Contracts ;
67using System . Runtime . CompilerServices ;
78
89#nullable enable
@@ -103,17 +104,15 @@ public static void IsNotEqualTo<T>(T value, T target, string name)
103104 /// <param name="name">The name of the input parameter being tested.</param>
104105 /// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is not a bitwise match for <paramref name="target"/>.</exception>
105106 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
106- public static void IsBitwiseEqualTo < T > ( T value , T target , string name )
107+ public static unsafe void IsBitwiseEqualTo < T > ( T value , T target , string name )
107108 where T : unmanaged
108109 {
109- // Include some fast paths if the input type is of size 1, 2, 4 or 8 .
110+ // Include some fast paths if the input type is of size 1, 2, 4, 8, or 16 .
110111 // In those cases, just reinterpret the bytes as values of an integer type,
111112 // and compare them directly, which is much faster than having a loop over each byte.
112113 // The conditional branches below are known at compile time by the JIT compiler,
113114 // so that only the right one will actually be translated into native code.
114- if ( typeof ( T ) == typeof ( byte ) ||
115- typeof ( T ) == typeof ( sbyte ) ||
116- typeof ( T ) == typeof ( bool ) )
115+ if ( sizeof ( T ) == 1 )
117116 {
118117 byte valueByte = Unsafe . As < T , byte > ( ref value ) ;
119118 byte targetByte = Unsafe . As < T , byte > ( ref target ) ;
@@ -125,9 +124,7 @@ public static void IsBitwiseEqualTo<T>(T value, T target, string name)
125124
126125 ThrowHelper . ThrowArgumentExceptionForsBitwiseEqualTo ( value , target , name ) ;
127126 }
128- else if ( typeof ( T ) == typeof ( ushort ) ||
129- typeof ( T ) == typeof ( short ) ||
130- typeof ( T ) == typeof ( char ) )
127+ else if ( sizeof ( T ) == 2 )
131128 {
132129 ushort valueUShort = Unsafe . As < T , ushort > ( ref value ) ;
133130 ushort targetUShort = Unsafe . As < T , ushort > ( ref target ) ;
@@ -139,9 +136,7 @@ public static void IsBitwiseEqualTo<T>(T value, T target, string name)
139136
140137 ThrowHelper . ThrowArgumentExceptionForsBitwiseEqualTo ( value , target , name ) ;
141138 }
142- else if ( typeof ( T ) == typeof ( uint ) ||
143- typeof ( T ) == typeof ( int ) ||
144- typeof ( T ) == typeof ( float ) )
139+ else if ( sizeof ( T ) == 4 )
145140 {
146141 uint valueUInt = Unsafe . As < T , uint > ( ref value ) ;
147142 uint targetUInt = Unsafe . As < T , uint > ( ref target ) ;
@@ -153,37 +148,66 @@ public static void IsBitwiseEqualTo<T>(T value, T target, string name)
153148
154149 ThrowHelper . ThrowArgumentExceptionForsBitwiseEqualTo ( value , target , name ) ;
155150 }
156- else if ( typeof ( T ) == typeof ( ulong ) ||
157- typeof ( T ) == typeof ( long ) ||
158- typeof ( T ) == typeof ( double ) )
151+ else if ( sizeof ( T ) == 8 )
159152 {
160153 ulong valueULong = Unsafe . As < T , ulong > ( ref value ) ;
161154 ulong targetULong = Unsafe . As < T , ulong > ( ref target ) ;
162155
163- if ( valueULong == targetULong )
156+ if ( Bit64Compare ( ref valueULong , ref targetULong ) )
164157 {
165158 return ;
166159 }
167160
168161 ThrowHelper . ThrowArgumentExceptionForsBitwiseEqualTo ( value , target , name ) ;
169162 }
170- else
163+ else if ( sizeof ( T ) == 16 )
171164 {
172- ref byte valueRef = ref Unsafe . As < T , byte > ( ref value ) ;
173- ref byte targetRef = ref Unsafe . As < T , byte > ( ref target ) ;
174- int bytesCount = Unsafe . SizeOf < T > ( ) ;
165+ ulong valueULong0 = Unsafe . As < T , ulong > ( ref value ) ;
166+ ulong targetULong0 = Unsafe . As < T , ulong > ( ref target ) ;
175167
176- for ( int i = 0 ; i < bytesCount ; i ++ )
168+ if ( Bit64Compare ( ref valueULong0 , ref targetULong0 ) )
177169 {
178- byte valueByte = Unsafe . Add ( ref valueRef , i ) ;
179- byte targetByte = Unsafe . Add ( ref targetRef , i ) ;
170+ ulong valueULong1 = Unsafe . Add ( ref Unsafe . As < T , ulong > ( ref value ) , 1 ) ;
171+ ulong targetULong1 = Unsafe . Add ( ref Unsafe . As < T , ulong > ( ref target ) , 1 ) ;
180172
181- if ( valueByte != targetByte )
173+ if ( Bit64Compare ( ref valueULong1 , ref targetULong1 ) )
182174 {
183- ThrowHelper . ThrowArgumentExceptionForsBitwiseEqualTo ( value , target , name ) ;
175+ return ;
184176 }
185177 }
178+
179+ ThrowHelper . ThrowArgumentExceptionForsBitwiseEqualTo ( value , target , name ) ;
180+ }
181+ else
182+ {
183+ Span < byte > valueBytes = new Span < byte > ( Unsafe . AsPointer ( ref value ) , sizeof ( T ) ) ;
184+ Span < byte > targetBytes = new Span < byte > ( Unsafe . AsPointer ( ref target ) , sizeof ( T ) ) ;
185+
186+ if ( valueBytes . SequenceEqual ( targetBytes ) )
187+ {
188+ return ;
189+ }
190+
191+ ThrowHelper . ThrowArgumentExceptionForsBitwiseEqualTo ( value , target , name ) ;
192+ }
193+ }
194+
195+ // Compares 64 bits of data from two given memory locations for bitwise equality
196+ [ Pure ]
197+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
198+ private static unsafe bool Bit64Compare ( ref ulong left , ref ulong right )
199+ {
200+ // Handles 32 bit case, because using ulong is inefficient
201+ if ( sizeof ( IntPtr ) == 4 )
202+ {
203+ ref int r0 = ref Unsafe . As < ulong , int > ( ref left ) ;
204+ ref int r1 = ref Unsafe . As < ulong , int > ( ref right ) ;
205+
206+ return r0 == r1 &&
207+ Unsafe . Add ( ref r0 , 1 ) == Unsafe . Add ( ref r1 , 1 ) ;
186208 }
209+
210+ return left == right ;
187211 }
188212
189213 /// <summary>
0 commit comments