0

My aim is to create a data structure which should have the following representation in the memory:

    int    countOfElements; // 4 bytes
    int    time_1;          // 4 bytes
    double price_1;         // 8 bytes
    int    time_2;          // 4 bytes
    double price_2;         // 8 bytes
    int    time_3;          // 4 bytes
    double price_3;         // 8 bytes
     . . . . .
    int    time_n;          // 4 bytes, n = countOfElements
    double price_n;         // 8 bytes, n = countOfElements

Unfortunately, I am not free to choose representation (field order and type) of this structure in the memory, because it relates another process on the OS. The data structure is intended to be placed on the shared memory by using WinAPI functions, and the purpose of this data structure is data exchange. Thus, I'll be also needed to get the pointer to this data structure in java... So, my effort to realize this data structure looks like:

import java.util.List;
import java.util.Arrays;
import com.sun.jna.Structure;

public class TQuote extends Structure {
    public int    time;
    public double price;

    @Override
    protected List getFieldOrder() {
        return Arrays.asList(new String[]{"time", "price"});
    }
}

public class TSharedArray extends Structure {
    public int      countOfElements;
    public TQuote[] elements;

    public TSharedArray(int size) {
        super();
        countOfElements = size;
        elements = new TQuote[size];
        for (int i = 0; i < size; i++) elements[i] = new TQuote();
    }

    @Override
    protected List getFieldOrder() {
        return Arrays.asList(new String[]{"countOfElements", "elements"});
    }
}

Though, my attempts to instantiate the structure TSharedArray structure = new TSharedArray(10); throws an exception:

/*
 Invalid Structure field in class xxx$TSharedArray, field name 'elements' (class xxx$TQuote): Can't instantiate class xxx$TQuote (java.lang.InstantiationException: xxx$TQuote)
    java.lang.IllegalArgumentException: Invalid Structure field in class xxx$TSharedArray, field name 'elements' (class xxx$TQuote): Can't instantiate class xxx$TQuote (java.lang.InstantiationException: xxx$TQuote)
        at com.sun.jna.Structure.validateField(Structure.java:962)
        at com.sun.jna.Structure.validateField(Structure.java:954)
        at com.sun.jna.Structure.validateFields(Structure.java:972)
        at com.sun.jna.Structure.<init>(Structure.java:186)
        at com.sun.jna.Structure.<init>(Structure.java:180)
        at com.sun.jna.Structure.<init>(Structure.java:167)
        at com.sun.jna.Structure.<init>(Structure.java:159)
        at xxx$TSharedArray.<init>(xxx.java:35)
        at xxx.onStart(xxx.java:57)
        at java.lang.Thread.run(Unknown Source)
*/

Java code in the xxx.java line #35 is super(); Any ideas, how to fix it?

Additionally, I noticed one strange thing... Let's assume, we have three structures and each of these structures contains of two fields:

public class TIntInt extends Structure {
    public int int1;
    public int int2;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList(new String[]{"int1", "int2"});
    }
}

public class TDblDbl extends Structure {
    public double dbl1;
    public double dbl2;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList(new String[]{"dbl1", "dbl2"});
    }
}

public class TIntDbl extends Structure {
    public int    int1;
    public double dbl1;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList(new String[]{"int1", "dbl1"});
    }
}

The first one (TIntInt) have size as expected of 2*4=8 bytes, the second one (TDblDbl) have size as expected of 2*8=16 bytes, but the third (TIntDbl - it's the same as TQuote) have size of 16 bytes instead of expected of 4+8=12. Why?:

System.out.println("TIntInt.size()=" + new TIntInt().size()); //  8 bytes, Ok
System.out.println("TDblDbl.size()=" + new TDblDbl().size()); // 16 bytes, Ok
System.out.println("TIntDbl.size()=" + new TIntDbl().size()); // 16 bytes!!! Why?

technomage
  • 9,861
  • 2
  • 26
  • 40
Andrius_S
  • 3
  • 2
  • I haven't used JNA but I can answer your last question. That structure is using 16 bytes because padding is being inserted to align the `double` on an 8-byte boundary. I'm not sure if you can control the padding. I added the `jna` tag which may help you get an answer to your question. – David Conrad Sep 30 '15 at 22:50
  • (I'm not sure the 'pointers', 'memory', or 'structure' tags are going to do much good; you're looking for JNA experts.) – David Conrad Sep 30 '15 at 22:51
  • @DavidConrad : If the padding isn't controllable, it imply that com.sun.jna.Structure is not proper means to achieve my goal :-(... – Andrius_S Sep 30 '15 at 23:42

3 Answers3

1

Any array fields within a Structure must be initialized. Note that an array size of zero is not supported.

public class TSharedArray extends Structure {
    public int      countOfElements;
    public TQuote[] elements = new TQuote[1];

    public TSharedArray() { 
        this(1); 
    }
    public TSharedArray(Pointer p) { 
        super(p);
        countOfElements = (int)readField("countOfElements");        
        elements = new TQuote[countOfElements];
        read();
    }
    public TSharedArray(int size) {
        countOfElements = size;
        elements = (TQuote[])new TQuote().toArray(size);
    }

    @Override
    protected List getFieldOrder() {
        return Arrays.asList(new String[]{"countOfElements", "elements"});
    }
}

As for your second question, the difference in size is due to structure padding. Usually fields will be aligned on a memory boundary equal to their size, thus there will be 4 bytes padding after your int field to align the double field on an 8-byte boundary.

EDIT

Any structures used as fields must have a public, no-args constructor (and it's a good idea to have a pointer-based constructor as well). The error you're seeing is because JNA can't create an instance of TQuote by reflection. You'll need to either address that issue or explicitly create a prepopulated array for that field:

public TQuote[] elements = (TQuote[])new TQuote().toArray(1);

Alignment

You can use Structure.ALIGN_NONE if you want to avoid the default platform alignment (see Structure.setAlignType()).

technomage
  • 9,861
  • 2
  • 26
  • 40
  • Thank you for the answer. Though, the test program with your code throws exactly the same Exception, just Line number changed: currently, an exception points to the constructor's header line `public TSharedArray(int size) {`... – Andrius_S Oct 02 '15 at 06:28
0

The amount of memory used is increased in 8 byte blocks. For more details see https://stackoverflow.com/a/321436/3915166

Community
  • 1
  • 1
Dmitry Baev
  • 2,270
  • 1
  • 14
  • 24
  • but the "structure" containing only one int field shows the size of 4 bytes... Ok I'll consider the 8-byte particularity in memory allocations for java objects. Thanks. – Andrius_S Sep 30 '15 at 23:26
0

After numerous attempts and experiments I found that the only code that runs without Exception is using Structure.ByReference:

public class TQuote extends Structure {
    public class ByReference extends TQuote implements Structure.ByReference {}
    public int    time;
    public double price;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList("time", "price");
    }
}

public class TSharedArray extends Structure {
  public int                  countOfElements;
  public TQuote.ByReference[] elements;

  public TSharedArray(int size) {
    super();
    countOfElements = size;
    elements = new TQuote.ByReference[size];
  }

  @Override
  protected List getFieldOrder() {
    return Arrays.asList("countOfElements", "elements");
  }
}

Though, the code above doesn't meet my goals because it forms an Array of References instead of an Array of Values. Thus, the memory representation of the structure is not proper.

I'm giving-up by trying to solve the problem with structures... Now I have another solution which doesn't use structures: I created the required memory allocation with the help of Pointers.

Andrius_S
  • 3
  • 2