Structure with (possibly) zero-length array

87 views
Skip to first unread message

Daniel Widdis

unread,
May 22, 2015, 6:32:33 PM5/22/15
to jna-...@googlegroups.com
I'm attempting to map the sysinfo structure from the Linux C standard library.  The structure definition includes this little gem at the end:

char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */

I have mapped this in my structure with a byte array:

public byte[] _f = new byte[20
   
- 2 * Native.getNativeSize(NativeLong.class)
    - Native.getNativeSize(int.class)]; // Padding to 64 bytes


Unfortunately, on 64-bit systems, sizeof(long) = 8 so the padding calculation equates to zero.  This is fine, as Java allows zero-length arrays.

However, when I attempt to use this, I execute the following code to allocate memory for the structure:

Sysinfo info = new Sysinfo();
Pointer p = new Memory(info.size());


This is failing because the Structure's size() method does not allow zero-length arrays.

Exception in thread "main" java.lang.IllegalArgumentException: Invalid Structure field in class Libc$Sysinfo, field name '_f', class [B: Arrays of length zero not allowed: class [B
 at com
.sun.jna.Structure.calculateSize(Structure.java:848)
 at com
.sun.jna.Structure.allocateMemory(Structure.java:279)
 at com
.sun.jna.Structure.ensureAllocated(Structure.java:268)
 at com
.sun.jna.Structure.size(Structure.java:307)

Is there a way I can work around this to ensure my structure is portable to both 32- and 64- bit systems?

Timothy Wall

unread,
May 22, 2015, 7:56:50 PM5/22/15
to jna-...@googlegroups.com
One thing you could do is override Structure.size() for this structure to pad out to 64 bytes only when necessary.
> --
> You received this message because you are subscribed to the Google Groups "Java Native Access" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Daniel Widdis

unread,
May 23, 2015, 1:25:35 PM5/23/15
to jna-...@googlegroups.com
Timothy,

Thanks for this great suggestion.

Unfortunately I was premature in determining how I needed to access the
structure size. Since the sysinfo() argument is only a pointer to the
structure, I had to do this:

Sysinfo info = new Sysinfo();

Pointer p = info.getPointer();

if (0 != Libc.INSTANCE.sysinfo(p)) ...


The getPointer() method eventually needs to calculate the size, but
through a different path than size():

Exception in thread "main" java.lang.IllegalArgumentException: Invalid Structure field in class oshi.software.os.linux.Libc$Sysinfo, field name '_f', class [B: Arrays of length zero not allowed: class [B

at com.sun.jna.Structure.calculateSize(Structure.java:848)

at com.sun.jna.Structure.allocateMemory(Structure.java:279)

at com.sun.jna.Structure.ensureAllocated(Structure.java:268)

at com.sun.jna.Structure.getPointer(Structure.java:326)


I have tried overriding calculateSize(), but this doesn't appear to work
because the ensureAllocated() method goes through a private method in
Structure. So I'd have to override/newly define the entire set of
methods starting with ensureAllocated().

At the moment I'm leaning toward defining separate structures and
instantiating either one in my code based on measuring sizeof(long).

Is there a fundamental reason that Structure can't handle 0-length arrays?

Timothy Wall

unread,
May 24, 2015, 9:21:53 PM5/24/15
to jna-...@googlegroups.com
By default, com.sun.na.Structure maps to ‘struct *’ when used as a parameter or return value. JNA also automatically syncs the Java fields with native memory before and after the function call, which it cannot do when you pass a raw Pointer.

Overriding `calculateSize` should be sufficient, i.e.

int calculateSize(int size) {
return super.calculateSize(size) + Platform.is64Bit() ? 0 : 32;
}

but I notice that’s package-protected; that really needs to be just protected.

You might be able to get away with making the 64-bit version have a non-zero size for the field, also long as you don’t ever try to use a contiguous array of them. The callee should ignore the extra memory; it certainly won’t ever write to it, and it doesn’t matter how the caller uses it b/c the memory is allocated.

Daniel Widdis

unread,
May 24, 2015, 10:19:52 PM5/24/15
to jna-...@googlegroups.com
Thanks! I've learned a few things...

Regarding the first paragraph, I did eventually get around to passing
the structure itself as an argument, but only after I'd already added in
the readField() calls for the elements I needed. Good to know I can
remove them.

And I did decide to do what you suggest in your last paragraph; simply
making the last padding element a byte[8] (max size for a 32 bit
system). On a 64 bit system Java will allocate 8 bytes of extra memory
that won't be written to on the native side, and I'll just ignore it.
Reply all
Reply to author
Forward
0 new messages