Sven Döring
Sven Döring

Reputation: 4378

Java Panama / Foreign Function & Memory API: Reading byte arrays of unknown length

Consider a header file defining this method:

ErrorCode_t GetBlob(Handle_const_t hHandle, uint8_t* const pValue, size_t* const pnSize);

This method writes n bytes into the array starting at the pointer of pValue. The size n will be set at the pointer of pnSize.

The header can be converted into Java source code using jextract, which gives:

public static long GetBlob(MemorySegment hHandle, MemorySegment pValue, MemorySegment pnSize) {
    // ..
}

After reading https://stackoverflow.com/questions/76978598 and https://stackoverflow.com/questions/71250218 I allocated a C_POINTER with unbounded address layout and called the method:

    MemorySegment handle = ..;
    MemorySegment arrayPointer = Arena.global().allocate(C_POINTER);
    MemorySegment sizePointer = Arena.global().allocate(JAVA_LONG);
    Accessor_h.GetBlob(handle, arrayPointer, sizePointer);

The call is successful. The sizePointer contains the size of the array.

But reading the array data fails. I tried the following:

    arrayPointer.get(C_POINTER, 0L)
        .toArray(JAVA_BYTE); // fails with "Segment is too large to wrap as byte[]. Size: 9223372036854775807"

Or:

    arrayPointer.get(C_POINTER, 0L)
            .asSlice(0L, sizePointer.get(JAVA_LONG, 0L))
            .toArray(JAVA_BYTE); // SIGSEGV (0xb)

Or:

    arrayPointer
            .toArray(JAVA_BYTE); // seems to contain the correct data, but only the first eight bytes

Or:

    arrayPointer
            .asSlice(0L, sizePointer.get(JAVA_LONG, 0L))
            .toArray(JAVA_BYTE); // fails with "java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment"

What can I do, to safely read the byte array in Java?

Upvotes: 1

Views: 236

Answers (1)

Jorn Vernee
Jorn Vernee

Reputation: 33895

The GetBlob function looks to be writing bytes into the pValue buffer you allocate with:

MemorySegment arrayPointer = Arena.global().allocate(C_POINTER);

This buffer is only 8 bytes in size (the size of C_POINTER.byteSize()). This is likely not what you want. Instead, you probably want to allocate an array of bytes, e.g.:

long n = ...;
MemorySegment arrayPointer = Arena.global().allocate(uint8_t, n);

Where n is the size of the buffer.

As for reading the result, your last attempt is the closest to being correct. Assuming that the array you allocate is larger than the size returned by the function.

    long bytesWritten = sizePointer.get(JAVA_LONG, 0L);
    byte[] arr = arrayPointer
            .asSlice(0L, bytesWritten) // will do bounds check
            .toArray(JAVA_BYTE);

If an IndexOutOfBoundsException is thrown by asSlice, it means that the GetBlob function overflowed the buffer you allocated, because bytesWritten would be larger than the buffer.

Upvotes: 0

Related Questions