@@ -33,6 +33,9 @@ public static partial class CTypes {
33
33
/// </summary>
34
34
[ PythonType , PythonHidden ]
35
35
public class StructType : PythonType , INativeType {
36
+
37
+ private enum LayoutKind { Msvc , Gcc }
38
+
36
39
[ DisallowNull ]
37
40
internal Field [ ] ? _fields ; // not null after type construction completes
38
41
private int ? _size , _alignment , _pack ;
@@ -252,6 +255,7 @@ internal static PythonType MakeSystemType(Type underlyingSystemType) {
252
255
return PythonType . SetPythonType ( underlyingSystemType , new StructType ( underlyingSystemType ) ) ;
253
256
}
254
257
258
+
255
259
[ MemberNotNull ( nameof ( _fields ) , nameof ( _size ) , nameof ( _alignment ) ) ]
256
260
private void SetFields ( object ? fields ) {
257
261
lock ( this ) {
@@ -263,11 +267,12 @@ private void SetFields(object? fields) {
263
267
List < Field > allFields = GetBaseSizeAlignmentAndFields ( out int size , out int alignment ) ;
264
268
265
269
IList < string > ? anonFields = GetAnonymousFields ( this ) ;
270
+ LayoutKind layout = GetStructLayout ( this ) ;
266
271
267
272
foreach ( object fieldDef in fieldDefList ) {
268
273
GetFieldInfo ( this , fieldDef , out string fieldName , out INativeType cdata , out bitCount ) ;
269
274
270
- int fieldOffset = UpdateSizeAndAlignment ( cdata , bitCount , ref lastType , ref size , ref alignment , ref curBitCount ) ;
275
+ int fieldOffset = UpdateSizeAndAlignment ( cdata , bitCount , layout , ref lastType , ref size , ref alignment , ref curBitCount ) ;
271
276
272
277
var newField = new Field ( fieldName , cdata , fieldOffset , allFields . Count , bitCount , curBitCount - bitCount ) ;
273
278
allFields . Add ( newField ) ;
@@ -292,6 +297,7 @@ private void SetFields(object? fields) {
292
297
}
293
298
}
294
299
300
+
295
301
internal static void CheckAnonymousFields ( List < Field > allFields , IList < string > ? anonFields ) {
296
302
if ( anonFields != null ) {
297
303
foreach ( string s in anonFields ) {
@@ -365,9 +371,10 @@ private List<Field> GetBaseSizeAlignmentAndFields(out int size, out int alignmen
365
371
foreach ( PythonType pt in BaseTypes ) {
366
372
if ( pt is StructType st ) {
367
373
st . EnsureFinal ( ) ;
374
+ LayoutKind layout = GetStructLayout ( st ) ;
368
375
foreach ( Field f in st . _fields ) {
369
376
allFields . Add ( f ) ;
370
- UpdateSizeAndAlignment ( f . NativeType , f . BitCount , ref lastType , ref size , ref alignment , ref totalBitCount ) ;
377
+ UpdateSizeAndAlignment ( f . NativeType , f . BitCount , layout , ref lastType , ref size , ref alignment , ref totalBitCount ) ;
371
378
372
379
if ( f . NativeType == this ) {
373
380
throw StructureCannotContainSelf ( ) ;
@@ -404,7 +411,7 @@ private List<Field> GetBaseSizeAlignmentAndFields(out int size, out int alignmen
404
411
/// On return, the count is updated with the number of occupied bits.</param>
405
412
/// <returns>
406
413
/// The offset of the processed field within the struct. If the processed field was a bitfield, this is the offset of its container unit.</returns>
407
- private int UpdateSizeAndAlignment ( INativeType cdata , int ? bitCount , ref INativeType ? lastType , ref int size , ref int alignment , ref int ? totalBitCount ) {
414
+ private int UpdateSizeAndAlignment ( INativeType cdata , int ? bitCount , LayoutKind layout , ref INativeType ? lastType , ref int size , ref int alignment , ref int ? totalBitCount ) {
408
415
int fieldOffset ;
409
416
if ( bitCount != null ) {
410
417
// process a bitfield
@@ -413,7 +420,7 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, ref INative
413
420
414
421
if ( _pack != null ) throw new NotImplementedException ( "pack with bitfields" ) ; // TODO: implement
415
422
416
- if ( UseMsvcBitfieldAlignmentRules ) {
423
+ if ( layout is LayoutKind . Msvc ) {
417
424
if ( totalBitCount != null ) { // there is already a bitfield container open
418
425
// under the MSVC rules, only bitfields of type that has the same size/alignment, are packed into the same container unit
419
426
if ( lastType ! . Size != cdata . Size || lastType . Alignment != cdata . Alignment ) {
@@ -443,7 +450,7 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, ref INative
443
450
totalBitCount = bitCount ;
444
451
lastType = cdata ;
445
452
}
446
- } else { // GCC bitfield alignment rules
453
+ } else if ( layout is LayoutKind . Gcc ) {
447
454
// under the GCC rules, all bitfields are packed into the same container unit or an overlapping container unit of a different type,
448
455
// as long as they fit and match the alignment
449
456
int containerOffset = AlignBack ( size , cdata . Alignment ) ; // TODO: _pack
@@ -460,6 +467,8 @@ private int UpdateSizeAndAlignment(INativeType cdata, int? bitCount, ref INative
460
467
fieldOffset = size = containerOffset ;
461
468
totalBitCount = containerBitCount + bitCount ;
462
469
lastType = cdata ;
470
+ } else {
471
+ throw new InvalidOperationException ( "unknown layout kind" ) ;
463
472
}
464
473
alignment = Math . Max ( alignment , lastType ! . Alignment ) ; // TODO: _pack
465
474
} else {
@@ -500,6 +509,20 @@ internal void EnsureFinal() {
500
509
}
501
510
}
502
511
512
+
513
+ private static LayoutKind GetStructLayout ( PythonType type ) {
514
+ if ( type . TryGetBoundAttr ( type . Context . SharedContext , type , "_layout_" , out object layout ) && layout is not null ) {
515
+ if ( Converter . TryConvertToString ( layout , out string ? layoutName ) ) {
516
+ if ( layoutName . StartsWith ( "ms" , StringComparison . Ordinal ) ) return LayoutKind . Msvc ;
517
+ if ( layoutName . StartsWith ( "gcc" , StringComparison . Ordinal ) ) return LayoutKind . Gcc ;
518
+ }
519
+ throw PythonOps . ValueError ( "unknown _layout_: {0}" , layout ) ;
520
+ }
521
+ // default layout for structs is platform dependent
522
+ return RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? LayoutKind . Msvc : LayoutKind . Gcc ;
523
+ }
524
+
525
+
503
526
/// <summary>
504
527
/// If our size/alignment hasn't been initialized then grabs the size/alignment
505
528
/// from all of our base classes. If later new _fields_ are added we'll be
@@ -525,9 +548,6 @@ private void EnsureSizeAndAlignment() {
525
548
526
549
private static int AlignBack ( int length , int size )
527
550
=> length & ~ ( size - 1 ) ;
528
-
529
- private static bool UseMsvcBitfieldAlignmentRules
530
- => RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
531
551
}
532
552
}
533
553
}
0 commit comments