Search code examples
javajava-20java-ffm

OpenJDK 20: JEP 434: Foreign Function & Memory API (Second Preview)


How to reinterpret MemorySegment in Java 20 Preview like Java 21 Preview MemorySegment:

MemorySegment reinterpret(long newSize) - Returns a new memory segment that has the same address and scope as this segment, but with the provided size?

Please refer to the following ccode snippet and the exception output details:

import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.util.Arrays;

// 434: Foreign Function & Memory API (Second Preview) // https://openjdk.org/jeps/434
class ForeignFunctionMemoryAPISecondPreview {
    private void radixsort() throws Throwable {

    // 1. Find foreign function on the C library path
    Linker linker          = Linker.nativeLinker();
    SymbolLookup stdlib    = linker.defaultLookup();
    MethodHandle radixsort = linker.downcallHandle(stdlib.find("radixsort").orElseThrow(), 
            FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_CHAR));

    // 2. Allocate on-heap memory to store four strings
    String[] javaStrings = { "mouse", "cat", "dog", "car" };
        System.out.println("radixsort input: " + Arrays.toString(javaStrings));

    // 3. Use try-with-resources to manage the lifetime of off-heap memory
    try (Arena offHeap = Arena.openConfined()) {
            // 4. Allocate a region of off-heap memory to store four pointers
            MemorySegment pointers = offHeap.allocateArray(ValueLayout.ADDRESS, javaStrings.length);

            // 5. Copy the strings from on-heap to off-heap
            for (int i = 0; i < javaStrings.length; i++) {
                MemorySegment cString = offHeap.allocateUtf8String(javaStrings[i]);
                pointers.setAtIndex(ValueLayout.ADDRESS, i, cString);
            }

            // 6. Sort the off-heap data by calling the foreign function
            radixsort.invoke(pointers, javaStrings.length, MemorySegment.NULL, '\0');

            // 7. Copy the (reordered) strings from off-heap to on-heap
            for (int i = 0; i < javaStrings.length; i++) {
                MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);
                // Java 21 Preview
        // cString = cString.reinterpret(Long.MAX_VALUE);
                // javaStrings[i] = cString.getUtf8String(0);
        System.out.println(cString);
            }
    }

    // 8. All off-heap memory is deallocated here
        System.out.println("radixsort output: " + Arrays.toString(javaStrings));
    }

    public static void main(String[] args) throws Throwable {
        System.out.println(String.format("Java Version: %s", System.getProperty("java.version")));

    var previewForeignFunctionMemoryAPISecond = new ForeignFunctionMemoryAPISecondPreview();
        previewForeignFunctionMemoryAPISecond.radixsort();
    }
}

// Output
/*
WARNING: Using incubator modules: jdk.incubator.vector, jdk.incubator.concurrent
Note: ForeignFunctionMemoryAPISecondPreview.java uses preview features of Java SE 20.
Note: Recompile with -Xlint:preview for details.
Java Version: 20.0.2
radixsort input: [mouse, cat, dog, car]
Exception in thread "main" java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ array: Optional.empty address:105553152688144 limit: 0 }; new offset = 0; new length = 1
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.outOfBoundException(AbstractMemorySegmentImpl.java:371)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:357)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:70)
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:124)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:448)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkBounds(AbstractMemorySegmentImpl.java:346)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkAccess(AbstractMemorySegmentImpl.java:311)
    at java.base/java.lang.invoke.VarHandleSegmentAsBytes.checkAddress(VarHandleSegmentAsBytes.java:81)
    at java.base/java.lang.invoke.VarHandleSegmentAsBytes.get(VarHandleSegmentAsBytes.java:108)
    at java.base/java.lang.foreign.MemorySegment.get(MemorySegment.java:1388)
    at java.base/jdk.internal.foreign.abi.SharedUtils.strlen(SharedUtils.java:199)
    at java.base/jdk.internal.foreign.abi.SharedUtils.toJavaStringInternal(SharedUtils.java:190)
    at java.base/java.lang.foreign.MemorySegment.getUtf8String(MemorySegment.java:890)
    at ForeignFunctionMemoryAPISecondPreview.radixsort(ForeignFunctionMemoryAPISecondPreview.java:43)
    at ForeignFunctionMemoryAPISecondPreview.main(ForeignFunctionMemoryAPISecondPreview.java:56)


  [1]: https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/lang/foreign/MemorySegment.html#reinterpret(long)

Solution

  • In java 20, you can use MemorySegment::ofAddress instead of reinterpret:

    cString = MemorySegment.ofAddress(cString.address(), Long.MAX_VALUE);