最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - What's the JNA mapping for 'unsigned char data[1]'? - Stack Overflow

programmeradmin0浏览0评论

I'm trying to write JNA wrappers for LibRaw and I'm stumbling over one particular C structure:

  typedef struct
  {
    enum LibRaw_image_formats type;
    ushort height, width, colors, bits;
    unsigned int data_size;
    unsigned char data[1];
  } libraw_processed_image_t;

... specifically the last member 'data[1]'. This seems to be a char (byte?) array which holds a large block of 8 or 16 bit data the size (and layout) of which is only determined at runtime. I would have expected to see something more like 'void *data' (which is used in other LibRaw structures) and would have called for a JNA declaration such as 'public Pointer data' with getByteArray or getShortArray to access the data in Java. However, the LibRaw API is what it is, so how do I declare the JNA equivalent?

On the (possibly flawed) assumption the 'char data[1]' and 'void *data' actually result in the same memory layout I tried using 'public Pointer data' in JNA, but then I get "Error: Invalid memory access" when I call getByteArray. I also tried using 'public byte[] data = new byte[1]', but that of course only gives me access to the first byte. I might get away with 'public byte[] data = new byte[SOME_BIG_NUMBER]', but that seems dangerous (even if it works) since I don't know how big the data is at the point in the runtime when the structure/class is instantiated.

So what the right way to do this?

Update: The SOME_BIG_NUMBER approach works (as in gives me access to the data) as long as SOME_BIG_NUMBER is the exact size of the data from the DLL. I can only get the actual size at runtime, so I'm left with the problem of how to define a JNA wrapper class with a runtime-defined array size when I don't call the constructor in my code (it's instantiated somewhere in JNA).

Another update: I solve the problem by changing the Java native function declaration to return a Pointer rather than a Structure of indeterminate size (JNA seemed happy with that) and then using a series of getInt, getShot and getByteArray to 'parse' the intended structure dynamically. I don't know if this is the best solution (or even a good one) but it seems to work.

I'm trying to write JNA wrappers for LibRaw and I'm stumbling over one particular C structure:

  typedef struct
  {
    enum LibRaw_image_formats type;
    ushort height, width, colors, bits;
    unsigned int data_size;
    unsigned char data[1];
  } libraw_processed_image_t;

... specifically the last member 'data[1]'. This seems to be a char (byte?) array which holds a large block of 8 or 16 bit data the size (and layout) of which is only determined at runtime. I would have expected to see something more like 'void *data' (which is used in other LibRaw structures) and would have called for a JNA declaration such as 'public Pointer data' with getByteArray or getShortArray to access the data in Java. However, the LibRaw API is what it is, so how do I declare the JNA equivalent?

On the (possibly flawed) assumption the 'char data[1]' and 'void *data' actually result in the same memory layout I tried using 'public Pointer data' in JNA, but then I get "Error: Invalid memory access" when I call getByteArray. I also tried using 'public byte[] data = new byte[1]', but that of course only gives me access to the first byte. I might get away with 'public byte[] data = new byte[SOME_BIG_NUMBER]', but that seems dangerous (even if it works) since I don't know how big the data is at the point in the runtime when the structure/class is instantiated.

So what the right way to do this?

Update: The SOME_BIG_NUMBER approach works (as in gives me access to the data) as long as SOME_BIG_NUMBER is the exact size of the data from the DLL. I can only get the actual size at runtime, so I'm left with the problem of how to define a JNA wrapper class with a runtime-defined array size when I don't call the constructor in my code (it's instantiated somewhere in JNA).

Another update: I solve the problem by changing the Java native function declaration to return a Pointer rather than a Structure of indeterminate size (JNA seemed happy with that) and then using a series of getInt, getShot and getByteArray to 'parse' the intended structure dynamically. I don't know if this is the best solution (or even a good one) but it seems to work.

Share Improve this question edited yesterday Daniel Widdis 9,13113 gold badges48 silver badges68 bronze badges asked yesterday Marek PMarek P 211 silver badge2 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 3

You've encountered a flexible array member. From the C Standard:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member ...

EXAMPLE: After the declaration:

struct s { int n; double d[]; };

the structure struct s has a flexible array member d. A typical way to use this is:

int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

and assuming that the call to malloc succeeds, the object pointed to by p behaves, for most purposes, as if p had been declared as:

struct { int n; double d[m]; } *p;

In JNA, you don't know the size of the last element until you read the size value, so the pattern is to read the size value, then re-allocate the array as the right size, and then read the whole structure a second time.

This question was asked at the JNA repository in Issue 449. In that case, this structure:

struct blob {
    size_t length;
    unsigned char data[];
};

was mapped in JNA like this:

class blob extends Structure {
    int length;
    byte[] data = new byte[1];
    public blob(int length) {
        this.length = length;
        this.data = new byte[length];
        allocateMemory();
    }
    public blob(Pointer p) {
        super(p);
        this.length = p.readInt(0);
        this.data = new byte[this.length];
        read();
    }
}

Note the size constructor when allocating on the JNA side, and pointer constructer when allocating based on a pointer to native-allocated memory.

So in your case the JNA mapping would be:

@FieldOrder({"type", "height", "width", "colors", "bits", "data_size", "data"})
public class LibrawProcessedImage extends Structure {
    public int type; // enum LibRaw_image_formats
    public short height;
    public short width;
    public short colors;
    public short bits;
    public int data_size;
    public byte[] data = new byte[1];

    public LibrawProcessedImage(Pointer p) {
        super(p);
        this.data_size = p.getInt(fieldOffset("data_size"));
        this.data = new byte[this.data_size];
        read();
    }

    // consider a constructor with size argument as per the above example
}
发布评论

评论列表(0)

  1. 暂无评论