2
2
using System . Collections . Generic ;
3
3
using System . Linq ;
4
4
using System . Reflection ;
5
+ using System . Runtime . InteropServices ;
5
6
using BenchmarkDotNet . Attributes ;
6
7
7
8
namespace BenchmarkDotNet . Extensions
@@ -242,12 +243,50 @@ internal static bool IsDefaultFasterThanField(this Type type, bool isClassicMono
242
243
_ when type . IsEnum
243
244
=> Enum . GetUnderlyingType ( type ) . SizeOfDefault ( ) ,
244
245
245
- // Note: the runtime pads structs for alignment purposes, and it enforces a minimum of 1 byte, even for empty structs,
246
- // but we don't need to worry about either of those cases for the purpose this serves (calculating whether to use `default` or read a field in Mono for the overhead method).
247
246
_ when type . IsValueType
248
- => type . GetFields ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) . Aggregate ( 0 , ( count , field ) => field . FieldType . SizeOfDefault ( ) + count ) ,
247
+ => type . SizeOfDefaultStruct ( ) ,
249
248
250
249
_ => throw new Exception ( "Unknown type size: " + type . FullName )
251
250
} ;
251
+
252
+ private static int SizeOfDefaultStruct ( this Type structType )
253
+ {
254
+ // Find the offset of the highest field, so we only have to calculate the size of it + its offset.
255
+ int fieldOffset = int . MinValue ;
256
+ FieldInfo maxField = null ;
257
+ bool containsReference = false ;
258
+ foreach ( var field in structType . GetFields ( BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) )
259
+ {
260
+ var fieldType = field . FieldType ;
261
+ containsReference |= fieldType . IsPointer || fieldType . IsClass || fieldType . IsInterface ;
262
+ int offset = field . GetFieldOffset ( ) ;
263
+ if ( offset > fieldOffset )
264
+ {
265
+ fieldOffset = offset ;
266
+ maxField = field ;
267
+ }
268
+ }
269
+ if ( maxField == null )
270
+ {
271
+ // Runtime enforces minimum struct size as 1 byte.
272
+ return 1 ;
273
+ }
274
+ // Runtime pads struct for alignment purposes if it contains a reference.
275
+ int structSize = maxField . FieldType . SizeOfDefault ( ) + fieldOffset ;
276
+ return containsReference
277
+ ? GetPaddedStructSize ( structSize )
278
+ : structSize ;
279
+ }
280
+
281
+ // Code obtained from https://stackoverflow.com/a/56512720
282
+ private static int GetFieldOffset ( this FieldInfo fi ) => Marshal . ReadInt32 ( fi . FieldHandle . Value + ( 4 + IntPtr . Size ) ) & 0xFFFFFF ;
283
+
284
+ private static int GetPaddedStructSize ( int fieldsSize )
285
+ {
286
+ Math . DivRem ( fieldsSize , IntPtr . Size , out int padding ) ;
287
+ return padding == 0
288
+ ? fieldsSize
289
+ : fieldsSize - padding + IntPtr . Size ;
290
+ }
252
291
}
253
292
}
0 commit comments