From 6de760fab005b9a7060ef5ec09ca8a224269414a Mon Sep 17 00:00:00 2001 From: Craig Warne Date: Tue, 27 Jan 2026 13:07:09 +1300 Subject: [PATCH 1/3] Expose BitOffset, update Offset to just be a getter which divides by 8 --- src/CppAst/CppField.cs | 11 ++++++++--- src/CppAst/CppModelBuilder.cs | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/CppAst/CppField.cs b/src/CppAst/CppField.cs index 1958c6af..32bbd9a6 100644 --- a/src/CppAst/CppField.cs +++ b/src/CppAst/CppField.cs @@ -71,11 +71,16 @@ public CppField(CppType type, string name) /// Gets or sets the number of bits for this bit field. Only valid if is true. /// public int BitFieldWidth { get; set; } - + + /// + /// Gets the offset of the field in bytes. + /// + public long Offset => BitOffset / 8; + /// - /// Gets or sets the offset of the field in bytes. + /// Gets or sets the offset of the field in bits. /// - public long Offset { get; set; } + public long BitOffset { get; set; } /// public override string ToString() diff --git a/src/CppAst/CppModelBuilder.cs b/src/CppAst/CppModelBuilder.cs index ba2ec604..fab1c4d7 100644 --- a/src/CppAst/CppModelBuilder.cs +++ b/src/CppAst/CppModelBuilder.cs @@ -551,7 +551,7 @@ static long Align(long offset, long alignment) Visibility = containerContext.CurrentVisibility, StorageQualifier = GetStorageQualifier(cursor), IsAnonymous = true, - Offset = offset, + BitOffset = offset, }; ParseAttributes(cursor, cppField, true); containerContext.DeclarationContainer.Fields.Add(cppField); @@ -1154,7 +1154,7 @@ private CppField VisitFieldOrVariable(CppContainerContext containerContext, CXCu cppField = previousField; cppField.Name = fieldName; cppField.Type = type; - cppField.Offset = cursor.OffsetOfField / 8; + cppField.BitOffset = cursor.OffsetOfField; } else { @@ -1164,7 +1164,7 @@ private CppField VisitFieldOrVariable(CppContainerContext containerContext, CXCu StorageQualifier = GetStorageQualifier(cursor), IsBitField = cursor.IsBitField, BitFieldWidth = cursor.FieldDeclBitWidth, - Offset = cursor.OffsetOfField / 8, + BitOffset = cursor.OffsetOfField, }; containerContext.DeclarationContainer.Fields.Add(cppField); ParseAttributes(cursor, cppField, true); @@ -1188,7 +1188,7 @@ private void AddAnonymousTypeWithField(CppContainerContext containerContext, CXC Visibility = GetVisibility(cursor.CXXAccessSpecifier), StorageQualifier = GetStorageQualifier(cursor), IsAnonymous = true, - Offset = cursor.OffsetOfField / 8, + BitOffset = cursor.OffsetOfField, }; containerContext.DeclarationContainer.Fields.Add(cppField); ParseAttributes(cursor, cppField, true); From bd05a98fcac7514f89d4486e0d1d331a3825a977 Mon Sep 17 00:00:00 2001 From: Craig Warne Date: Thu, 29 Jan 2026 09:38:55 +1300 Subject: [PATCH 2/3] Fix for anonymous field offsets (need to convert between byte and bit offsets in calculations) --- src/CppAst/CppModelBuilder.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/CppAst/CppModelBuilder.cs b/src/CppAst/CppModelBuilder.cs index fab1c4d7..fadb1d30 100644 --- a/src/CppAst/CppModelBuilder.cs +++ b/src/CppAst/CppModelBuilder.cs @@ -531,13 +531,14 @@ private CXChildVisitResult VisitMember(CXCursor cursor, CXCursor parent, void* d // Might not be always correct (with alignment rules), // but not sure how to recover the offset without recalculating the entire offsets // BF: disregard the above comment? as we are now aligning using the anonymous struct's AlignOf... - var offset = 0L; + var bitOffset = 0L; var cppClassContainer = containerContext.Container as CppClass; if (cppClassContainer is object && cppClassContainer.Fields.Count > 0) { var lastField = cppClassContainer.Fields[cppClassContainer.Fields.Count - 1]; - offset = (int)lastField.Offset + lastField.Type.SizeOf; + var offset = lastField.Offset + lastField.Type.SizeOf; offset = Align(offset, cppClass.AlignOf); + bitOffset = offset * 8; static long Align(long offset, long alignment) { @@ -551,7 +552,7 @@ static long Align(long offset, long alignment) Visibility = containerContext.CurrentVisibility, StorageQualifier = GetStorageQualifier(cursor), IsAnonymous = true, - BitOffset = offset, + BitOffset = bitOffset, }; ParseAttributes(cursor, cppField, true); containerContext.DeclarationContainer.Fields.Add(cppField); From a4008c14cf090ef6c706156df0941605564f4d9e Mon Sep 17 00:00:00 2001 From: Craig Warne Date: Thu, 29 Jan 2026 09:59:40 +1300 Subject: [PATCH 3/3] Add basic unit test for bit offsets --- src/CppAst.Tests/TestStructs.cs | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/CppAst.Tests/TestStructs.cs b/src/CppAst.Tests/TestStructs.cs index 2bfebf7b..0810837a 100644 --- a/src/CppAst.Tests/TestStructs.cs +++ b/src/CppAst.Tests/TestStructs.cs @@ -310,5 +310,46 @@ struct HelloWorld } ); } + + [Test] + public void TestBitOffsets() + { + ParseAssert(@" +struct NormalFields +{ + int a; + short b; + float c; +}; + +struct Bitfields +{ + int a : 1; + int b : 2; + int c : 3; +}; +", + compilation => + { + Assert.False(compilation.HasErrors); + + void Test(int classIndex, int fieldIndex, int expectedOffset, int expectedBitOffset) + { + var cppStruct = compilation.Classes[classIndex]; + var field = cppStruct.Fields[fieldIndex]; + Assert.AreEqual(expectedOffset, field.Offset); + Assert.AreEqual(expectedBitOffset, field.BitOffset); + } + + Test(0, 0, 0, 0); + Test(0, 1, 4, 32); + Test(0, 2, 8, 64); + + Test(1, 0, 0, 0); + Test(1, 1, 0, 1); + Test(1, 2, 0, 3); + } + ); + } } }