diff --git a/libtest/StructNestedTest.c b/libtest/StructNestedTest.c new file mode 100644 index 000000000..0d59ea95a --- /dev/null +++ b/libtest/StructNestedTest.c @@ -0,0 +1,129 @@ +#include +#include +#include + +typedef struct _A { + int x; + char y; +} A; + +typedef struct _B { + char x; + int y; +} B; + +typedef struct _C { + char x; + char y; + int z; +} C; + +typedef struct _D { + char x; + long long y; + int z; +} D; + +typedef struct _E { + char x; + D y; +} E; + +typedef struct _Array1 { + D t[3]; +} Array1; + +typedef union _MyLargeInteger { + + struct { + unsigned int LowPart; + int HighPart; + } u; + long long QuadPart; +} MyLargeInteger; + +typedef struct _F { + int x; + MyLargeInteger y; +} F; + +typedef struct _G { + char x; + MyLargeInteger y; + int z; +} G; + +typedef struct _Array2 { + G t[3]; +} Array2; + +typedef union _Union1 { + int intVal[2]; + char ch[8]; + MyLargeInteger my; + short ss[4]; + long long u; +} Union1; + +typedef struct _H { + char x[3]; +} H; + +typedef union _Union2 { + H x[5]; +} Union2; + +typedef struct _J { + short x; + char y[3]; +} J; + +typedef union _Union3 { + J x[5]; + char y[13]; +} Union3; + +struct Result { + int align; + int size; +}; + +#define DUMP(type) \ +do { \ +typedef struct _AlignType##type { \ + char c; \ + type d; \ +} AlignType##type; \ +if(strcmp(#type,name)==0){ \ + res.align = offsetof(AlignType##type, d); \ + res.size = sizeof(type); \ +} \ +} while(0) + +static struct Result getTypeDescription(const char * name) { + struct Result res = {0, 0}; + DUMP(A); + DUMP(B); + DUMP(C); + DUMP(D); + DUMP(E); + DUMP(Array1); + DUMP(MyLargeInteger); + DUMP(F); + DUMP(G); + DUMP(Array2); + DUMP(Union1); + DUMP(H); + DUMP(Union2); + DUMP(J); + DUMP(Union3); + return res; +} + +int getTypeSize(const char * name) { + return getTypeDescription(name).size; +} + +int getTypeAlign(const char * name) { + return getTypeDescription(name).align; +} diff --git a/src/main/java/jnr/ffi/Struct.java b/src/main/java/jnr/ffi/Struct.java index f235472cb..33cbadb60 100755 --- a/src/main/java/jnr/ffi/Struct.java +++ b/src/main/java/jnr/ffi/Struct.java @@ -53,7 +53,7 @@ static final class Info { int size = 0; int minAlign = 1; boolean isUnion = false; - boolean resetIndex = false; + int currentSize = -1; // only used when this is an enum and add array elements Alignment alignment = new Alignment(0); @@ -93,6 +93,20 @@ private jnr.ffi.Pointer allocateMemory(int flags) { } } + private int nextOffset(int size, int alignment) { + if (isUnion) { + if (currentSize > -1) { // add element to an array + int result = align(this.currentSize, alignment); + this.currentSize = result + size; + return result; + } else { + return 0; + } + } else { + return align(this.size, alignment); + } + } + public final void useMemory(jnr.ffi.Pointer io) { this.memory = io; } @@ -105,7 +119,7 @@ protected final int addField(int sizeBits, int alignBits, Offset offset) { protected final int addField(int sizeBits, int alignBits) { final int alignment = this.alignment.intValue() > 0 ? Math.min(this.alignment.intValue(), (alignBits >> 3)) : (alignBits >> 3); - final int offset = resetIndex ? 0 : align(this.size, alignment); + final int offset = nextOffset(sizeBits >> 3, alignment); this.size = Math.max(this.size, offset + (sizeBits >> 3)); this.minAlign = Math.max(this.minAlign, alignment); return offset; @@ -139,7 +153,7 @@ protected Struct(Runtime runtime, Struct enclosing) { */ protected Struct(Runtime runtime, final boolean isUnion) { this(runtime); - __info.resetIndex = isUnion; + __info.currentSize = -1; __info.isUnion = isUnion; } @@ -314,14 +328,14 @@ protected abstract class Member { * Starts an array construction session */ protected final void arrayBegin() { - __info.resetIndex = false; + __info.currentSize = 0; } /** * Ends an array construction session */ protected final void arrayEnd() { - __info.resetIndex = __info.isUnion; + __info.currentSize = -1; } /** @@ -684,10 +698,11 @@ protected UTF8String[] array(UTF8String[] array, int stringLength) { protected final T inner(T struct) { int alignment = __info.alignment.intValue() > 0 ? Math.min(__info.alignment.intValue(), struct.__info.getMinimumAlignment()) : struct.__info.getMinimumAlignment(); - int offset = __info.resetIndex ? 0 : align(__info.size, alignment); + int offset = __info.nextOffset(struct.__info.size, alignment); struct.__info.enclosing = this; struct.__info.offset = offset; __info.size = Math.max(__info.size, offset + struct.__info.size); + __info.minAlign = Math.max(__info.minAlign, alignment); return struct; } diff --git a/src/main/java/jnr/ffi/StructLayout.java b/src/main/java/jnr/ffi/StructLayout.java index a1b32cf0b..4a9a5c7ca 100644 --- a/src/main/java/jnr/ffi/StructLayout.java +++ b/src/main/java/jnr/ffi/StructLayout.java @@ -32,7 +32,7 @@ public class StructLayout extends Type { private final Runtime runtime; private final boolean isUnion = false; - private boolean resetIndex = false; + private int currentSize = -1; // only used when this is an enum and add array elements StructLayout enclosing = null; int offset = 0; int size = 0; @@ -100,9 +100,22 @@ private static int align(int offset, int alignment) { return (offset + alignment - 1) & ~(alignment - 1); } + private int nextOffset(int size, int alignment) { + if (isUnion) { + if (currentSize > -1) { // add element to an array + int result = align(this.currentSize, alignment); + this.currentSize = result + size; + return result; + } else { + return 0; + } + } else { + return align(this.size, alignment); + } + } protected final int addField(int size, int align) { - final int off = resetIndex ? 0 : align(this.size, align); + final int off = nextOffset(size, align); this.size = Math.max(this.size, off + size); this.alignment = Math.max(this.alignment, align); this.paddedSize = align(this.size, this.alignment); @@ -162,14 +175,14 @@ public final long offset() { * Starts an array construction session */ protected final void arrayBegin() { - resetIndex = false; + this.currentSize = 0; } /** * Ends an array construction session */ protected final void arrayEnd() { - resetIndex = isUnion; + this.currentSize = -1; } /** @@ -201,7 +214,7 @@ protected final T inner(T structLayout) { structLayout.offset = align(this.size, structLayout.alignment); this.size = structLayout.offset + structLayout.size; this.paddedSize = align(this.size, this.alignment()); - + this.alignment = Math.max(this.alignment, structLayout.alignment); return structLayout; } diff --git a/src/test/java/jnr/ffi/StructTest.java b/src/test/java/jnr/ffi/StructTest.java new file mode 100644 index 000000000..8162f89e9 --- /dev/null +++ b/src/test/java/jnr/ffi/StructTest.java @@ -0,0 +1,227 @@ +package jnr.ffi; + +import static org.junit.Assert.assertEquals; +import org.junit.BeforeClass; +import org.junit.Test; + +public class StructTest { + + public static interface TestLib { + int getTypeSize(String type); + int getTypeAlign(String type); + } + public static class A extends Struct { + private Signed32 x = new Signed32(); + private Signed8 y = new Signed8(); + public A(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class B extends Struct { + private Signed8 x = new Signed8(); + private Signed32 y = new Signed32(); + public B(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class C extends Struct { + private Signed8 x = new Signed8(); + private Signed8 y = new Signed8(); + private Signed32 z = new Signed32(); + public C(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class D extends Struct { + private Signed8 x = new Signed8(); + private Signed64 y = new Signed64(); + private Signed32 z = new Signed32(); + public D(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class E extends Struct { + private Signed8 x = new Signed8(); + private D y = inner(new D(getRuntime())); + public E(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class Array1 extends Struct { + private D[] t = array(new D[3]); + public Array1(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class LARGE_INTEGER_PART extends Struct { + private Unsigned32 LowPart = new Unsigned32(); + private Signed32 HighPart = new Signed32(); + public LARGE_INTEGER_PART(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class MyLargeInteger extends Union { // union + private LARGE_INTEGER_PART u = inner(new LARGE_INTEGER_PART(getRuntime())); + private Signed64 QuadPart = new Signed64(); + public MyLargeInteger(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class F extends Struct { + private Signed32 x = new Signed32(); + private MyLargeInteger y = inner(new MyLargeInteger(getRuntime())); + public F(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class G extends Struct { + private Signed8 x = new Signed8(); + private MyLargeInteger y = inner(new MyLargeInteger(getRuntime())); + private Signed32 z = new Signed32(); + public G(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class Array2 extends Struct { + private G[] t = array(new G[3]); + Array2(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class Union1 extends Union { + private Signed32[] intVal = array(new Signed32[2]); + private Signed8[] ch = array(new Signed8[8]); + private MyLargeInteger my = inner(new MyLargeInteger(getRuntime())); + private Signed16[] ss = array(new Signed16[4]); + private Signed64 u = new Signed64(); + public Union1(jnr.ffi.Runtime runtime) { + super(runtime); + } + } + public static class H extends Struct { + private Signed8[] x = array(new Signed8[3]); + public H(Runtime runtime) { + super(runtime); + } + } + public static class Union2 extends Union { + private H[] x = array(new H[5]); + public Union2(Runtime runtime) { + super(runtime); + } + } + public static class J extends Struct { + private Signed16 x = new Signed16(); + private Signed8[] y = array(new Signed8[3]); + public J(Runtime runtime) { + super(runtime); + } + } + public static class Union3 extends Union { + private J[] x = array(new J[5]); + private Signed8[] y = array(new Signed8[13]); + public Union3(Runtime runtime) { + super(runtime); + } + } + + private static jnr.ffi.Runtime runtime; + + static TestLib testlib; + + @BeforeClass + public static void setUpClass() { + testlib = TstUtil.loadTestLib(TestLib.class); + runtime = jnr.ffi.Runtime.getRuntime(testlib); + } + + @Test + public void testAlignA() { + test(new A(runtime)); + } + + @Test + public void testAlignB() { + test(new B(runtime)); + } + + @Test + public void testAlignC() { + test(new C(runtime)); + } + + @Test + public void testAlignD() { + test(new D(runtime)); + } + + @Test + public void testAlignE() { + test(new E(runtime)); + } + + @Test + public void testAlignArray1() { + test(new Array1(runtime)); + } + + @Test + public void testAlignMyLargeInteger() { + test(new MyLargeInteger(runtime)); + } + + @Test + public void testAlignF() { + test(new F(runtime)); + } + + @Test + public void testAlignG() { + test(new G(runtime)); + } + + @Test + public void testAlignArray2() { + test(new Array2(runtime)); + } + + @Test + public void testAlignUnion1() { + test(new Union1(runtime)); + } + + @Test + public void testAlignH() { + test(new H(runtime)); + } + + @Test + public void testAlignUnion2() { + test(new Union2(runtime)); + } + + @Test + public void testAlignJ() { + test(new J(runtime)); + } + + @Test + public void testAlignUnion3() { + test(new Union3(runtime)); + } + + private void test(Struct a) { + String name = a.getClass().getSimpleName(); + int alignment = testlib.getTypeAlign(name); + int sizeof = testlib.getTypeSize(name); + assertEquals(name + ".alignment()", alignment, Struct.alignment(a)); + assertEquals("sizeof(" + name + ")", sizeof, sizeof(a)); + } + + private int sizeof(Struct struct) { + int size = Struct.size(struct); + int align = Struct.alignment(struct); + return (size + align - 1) & -align; + } + +}