diff --git a/src/java.base/share/classes/java/lang/foreign/Arena.java b/src/java.base/share/classes/java/lang/foreign/Arena.java index 287f466b7f7..f111368d7d5 100644 --- a/src/java.base/share/classes/java/lang/foreign/Arena.java +++ b/src/java.base/share/classes/java/lang/foreign/Arena.java @@ -48,7 +48,7 @@ import java.lang.foreign.MemorySegment.Scope; * accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the * global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread. * {@snippet lang = java: - * MemorySegment segment = Arena.global().allocate(100, 1); + * MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()' * ... * // segment is never deallocated! *} @@ -58,9 +58,8 @@ import java.lang.foreign.MemorySegment.Scope; * of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time * after the automatic arena (and all the segments allocated by it) become * unreachable, as shown below: - * * {@snippet lang = java: - * MemorySegment segment = Arena.ofAuto().allocate(100, 1); + * MemorySegment segment = Arena.ofAuto().allocate(100, 1); // @highlight regex='ofAuto()' * ... * segment = null; // the segment region becomes available for deallocation after this point *} @@ -77,7 +76,7 @@ import java.lang.foreign.MemorySegment.Scope; * * {@snippet lang = java: * MemorySegment segment = null; - * try (Arena arena = Arena.ofConfined()) { + * try (Arena arena = Arena.ofConfined()) { // @highlight regex='ofConfined()' * segment = arena.allocate(100); * ... * } // segment region deallocated here @@ -157,24 +156,25 @@ import java.lang.foreign.MemorySegment.Scope; * * {@snippet lang = java: * class SlicingArena implements Arena { - * final Arena arena = Arena.ofConfined(); - * final SegmentAllocator slicingAllocator; + * final Arena arena = Arena.ofConfined(); + * final SegmentAllocator slicingAllocator; * - * SlicingArena(long size) { - * slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size)); - * } + * SlicingArena(long size) { + * slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size)); + * } * - * public void allocate(long byteSize, long byteAlignment) { - * return slicingAllocator.allocate(byteSize, byteAlignment); - * } + * public MemorySegment allocate(long byteSize, long byteAlignment) { + * return slicingAllocator.allocate(byteSize, byteAlignment); + * } * - * public MemorySegment.Scope scope() { - * return arena.scope(); - * } + * public MemorySegment.Scope scope() { + * return arena.scope(); + * } + * + * public void close() { + * arena.close(); + * } * - * public void close() { - * return arena.close(); - * } * } * } * @@ -183,10 +183,10 @@ import java.lang.foreign.MemorySegment.Scope; * * {@snippet lang = java: * try (Arena slicingArena = new SlicingArena(1000)) { - * for (int i = 0 ; i < 10 ; i++) { - * MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5); - * ... - * } + * for (int i = 0; i < 10; i++) { + * MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5); + * ... + * } * } // all memory allocated is released here * } * @@ -253,8 +253,8 @@ public interface Arena extends SegmentAllocator, AutoCloseable { * {@code S1, S2} returned by this method, the following invariant must hold: * * {@snippet lang = java: - * S1.overlappingSlice(S2).isEmpty() == true - *} + * S1.asOverlappingSlice(S2).isEmpty() == true + * } * * @param byteSize the size (in bytes) of the off-heap memory block backing the native memory segment. * @param byteAlignment the alignment constraint (in bytes) of the off-heap region of memory backing the native memory segment. diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index d192c46148a..8a57f51d150 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -77,7 +77,7 @@ import java.util.stream.Stream; * {@snippet lang = java: * Linker linker = Linker.nativeLinker(); * MethodHandle strlen = linker.downcallHandle( - * linker.defaultLookup().find("strlen").get(), + * linker.defaultLookup().find("strlen").orElseThrow(), * FunctionDescriptor.of(JAVA_LONG, ADDRESS) * ); * } @@ -91,9 +91,9 @@ import java.util.stream.Stream; * The obtained downcall method handle is then invoked as follows: * * {@snippet lang = java: - * try (Arena arena = Arena.openConfined()) { + * try (Arena arena = Arena.ofConfined()) { * MemorySegment str = arena.allocateUtf8String("Hello"); - * long len = strlen.invoke(str); // 5 + * long len = (long) strlen.invokeExact(str); // 5 * } * } *

Describing C signatures

@@ -226,7 +226,7 @@ import java.util.stream.Stream; * {@snippet lang = java: * Linker linker = Linker.nativeLinker(); * MethodHandle qsort = linker.downcallHandle( - * linker.defaultLookup().find("qsort").get(), + * linker.defaultLookup().find("qsort").orElseThrow(), * FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS) * ); * } @@ -240,7 +240,7 @@ import java.util.stream.Stream; * * {@snippet lang = java: * class Qsort { - * static int qsortCompare(MemorySegment elem1, MemorySegmet elem2) { + * static int qsortCompare(MemorySegment elem1, MemorySegment elem2) { * return Integer.compare(elem1.get(JAVA_INT, 0), elem2.get(JAVA_INT, 0)); * } * } @@ -268,7 +268,7 @@ import java.util.stream.Stream; * {@snippet lang = java: * try (Arena arena = Arena.ofConfined()) { * MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena); - * MemorySegment array = session.allocateArray(0, 9, 3, 4, 6, 5, 1, 8, 2, 7); + * MemorySegment array = arena.allocateArray(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7); * qsort.invokeExact(array, 10L, 4L, comparFunc); * int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] * } @@ -307,12 +307,12 @@ import java.util.stream.Stream; * Linker linker = Linker.nativeLinker(); * * MethodHandle malloc = linker.downcallHandle( - * linker.defaultLookup().find("malloc").get(), + * linker.defaultLookup().find("malloc").orElseThrow(), * FunctionDescriptor.of(ADDRESS, JAVA_LONG) * ); * * MethodHandle free = linker.downcallHandle( - * linker.defaultLookup().find("free").get(), + * linker.defaultLookup().find("free").orElseThrow(), * FunctionDescriptor.ofVoid(ADDRESS) * ); * } @@ -334,9 +334,15 @@ import java.util.stream.Stream; * method, as follows: * * {@snippet lang = java: - * MemorySegment allocateMemory(long byteSize, Arena arena) { - * MemorySegment segment = (MemorySegment)malloc.invokeExact(byteSize); // size = 0, scope = always alive - * return segment.reinterpret(byteSize, arena, s -> free.invokeExact(s)); // size = byteSize, scope = arena.scope() + * MemorySegment allocateMemory(long byteSize, Arena arena) throws Throwable { + * MemorySegment segment = (MemorySegment) malloc.invokeExact(byteSize); // size = 0, scope = always alive + * return segment.reinterpret(byteSize, arena, s -> { + * try { + * free.invokeExact(s); + * } catch (Throwable e) { + * throw new RuntimeException(e); + * } + * }); // size = byteSize, scope = arena.scope() * } * } * @@ -389,7 +395,7 @@ import java.util.stream.Stream; * {@snippet lang = java: * Linker linker = Linker.nativeLinker(); * MethodHandle printf = linker.downcallHandle( - * linker.defaultLookup().lookup("printf").get(), + * linker.defaultLookup().find("printf").orElseThrow(), * FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT, JAVA_INT), * Linker.Option.firstVariadicArg(1) // first int is variadic * ); @@ -616,12 +622,12 @@ public sealed interface Linker permits AbstractLinker { * Linker.Option ccs = Linker.Option.captureCallState("errno"); * MethodHandle handle = Linker.nativeLinker().downcallHandle(targetAddress, FunctionDescriptor.ofVoid(), ccs); * - * StructLayout capturedStateLayout = Linker.Option.capturedStateLayout(); + * StructLayout capturedStateLayout = Linker.Option.captureStateLayout(); * VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno")); * try (Arena arena = Arena.ofConfined()) { * MemorySegment capturedState = arena.allocate(capturedStateLayout); * handle.invoke(capturedState); - * int errno = errnoHandle.get(capturedState); + * int errno = (int) errnoHandle.get(capturedState); * // use errno * } * } diff --git a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java index 0734d3678da..78f3e6758f6 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -660,20 +660,20 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin * padding layout} elements. As such, the following struct layout creation will fail with an exception: * * {@snippet lang = java: - * structLayout(JAVA_SHORT, JAVA_INT) + * structLayout(JAVA_SHORT, JAVA_INT); * } * * To avoid the exception, clients can either insert additional padding layout elements: * * {@snippet lang = java: - * structLayout(JAVA_SHORT, MemoryLayout.ofPadding(2), JAVA_INT) + * structLayout(JAVA_SHORT, MemoryLayout.paddingLayout(2), JAVA_INT); * } * * Or, alternatively, they can use a member layout which features a smaller alignment constraint. This will result * in a packed struct layout: * * {@snippet lang = java: - * structLayout(JAVA_SHORT, JAVA_INT.withByteAlignment(2)) + * structLayout(JAVA_SHORT, JAVA_INT.withByteAlignment(2)); * } */ static StructLayout structLayout(MemoryLayout... elements) { diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 7efc8ef8da0..36e2decc4dd 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -142,7 +142,7 @@ import jdk.internal.vm.annotation.ForceInline; * MethodType.methodType(long.class, long.class, long.class)); * intHandle = MethodHandles.filterCoordinates(intHandle, 1, * MethodHandles.insertArguments(multiplyExact, 0, 4L)); - * intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 + * int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 * } * * Alternatively, complex var handles can can be obtained @@ -152,7 +152,7 @@ import jdk.internal.vm.annotation.ForceInline; * {@snippet lang=java : * MemorySegment segment = ... * VarHandle intHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(); - * intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 + * int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 * } * *

Slicing memory segments

@@ -756,8 +756,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * segment. Equivalent to (but likely more efficient than) the following code: * * {@snippet lang=java : - * byteHandle = MemoryLayout.ofSequence(ValueLayout.JAVA_BYTE) - * .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); + * var byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) + * .varHandle(MemoryLayout.PathElement.sequenceElement()); * for (long l = 0; l < segment.byteSize(); l++) { * byteHandle.set(segment.address(), l, value); * } @@ -785,7 +785,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { *

* Calling this method is equivalent to the following code: * {@snippet lang=java : - * MemorySegment.copy(src, 0, this, 0, src.byteSize); + * MemorySegment.copy(src, 0, this, 0, src.byteSize()); * } * @param src the source segment. * @throws IndexOutOfBoundsException if {@code src.byteSize() > this.byteSize()}. diff --git a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java index 61ddd50e0a1..88163d61054 100644 --- a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java @@ -117,8 +117,8 @@ public sealed interface ValueLayout extends MemoryLayout permits *

* Consider the following access expressions: * {@snippet lang=java : - * int value1 = arrayHandle.get(10, 2, 4); // ok, accessed offset = 8176 - * int value2 = arrayHandle.get(0, 0, 30); // out of bounds value for z + * int value1 = (int) arrayHandle.get(10, 2, 4); // ok, accessed offset = 8176 + * int value2 = (int) arrayHandle.get(0, 0, 30); // out of bounds value for z * } * In the first case, access is well-formed, as the values for {@code x}, {@code y} and {@code z} conform to * the bounds specified above. In the second case, access fails with {@link IndexOutOfBoundsException}, diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index a0ec03854c9..c905be4c249 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -89,7 +89,7 @@ * Linker linker = Linker.nativeLinker(); * SymbolLookup stdlib = linker.defaultLookup(); * MethodHandle strlen = linker.downcallHandle( - * stdlib.find("strlen").get(), + * stdlib.find("strlen").orElseThrow(), * FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS) * ); * diff --git a/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java b/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java new file mode 100644 index 00000000000..b2143fe3c55 --- /dev/null +++ b/src/java.base/share/classes/java/lang/foreign/snippet-files/Snippets.java @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.foreign.snippets; + +import java.lang.foreign.AddressLayout; +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.SequenceLayout; +import java.lang.foreign.StructLayout; +import java.lang.foreign.SymbolLookup; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static java.lang.foreign.MemoryLayout.sequenceLayout; +import static java.lang.foreign.MemoryLayout.structLayout; +import static java.lang.foreign.SymbolLookup.libraryLookup; +import static java.lang.foreign.SymbolLookup.loaderLookup; +import static java.lang.foreign.ValueLayout.*; +import static java.nio.ByteOrder.BIG_ENDIAN; + +/** + * Snippets for the java.lang.foreign documentation. + */ +class Snippets { + + /** + * Creates a new snippet. + */ + public Snippets() { + } + + static class ArenaSnippets { + + void globalArena() { + // @start region="global-allocation": + MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()' + // ... + // segment is never deallocated! + // @end + } + + void autoArena() { + // @start region="auto-allocation": + MemorySegment segment = Arena.ofAuto().allocate(100, 1); // @highlight regex='ofAuto()' + // ... + segment = null; // the segment region becomes available for deallocation after this point + // @end + } + + void confinedArena() { + // @start region="confined-allocation": + MemorySegment segment = null; + try (Arena arena = Arena.ofConfined()) { // @highlight regex='ofConfined()' + segment = arena.allocate(100); + // ... + } // segment region deallocated here + segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException + // @end + } + + static + // @start region="slicing-arena": + class SlicingArena implements Arena { + final Arena arena = Arena.ofConfined(); + final SegmentAllocator slicingAllocator; + + SlicingArena(long size) { + slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size)); + } + + public MemorySegment allocate(long byteSize, long byteAlignment) { + return slicingAllocator.allocate(byteSize, byteAlignment); + } + + public MemorySegment.Scope scope() { + return arena.scope(); + } + + public void close() { + arena.close(); + } + + } + // @end + + public static void main(String[] args) { + // @start region="slicing-arena-main": + try (Arena slicingArena = new SlicingArena(1000)) { + for (int i = 0; i < 10; i++) { + MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5); + // ... + } + } // all memory allocated is released here + // @end + } + + void arenaOverlap() { + try (var arena = Arena.ofConfined()) { + var S1 = arena.allocate(16L); + var S2 = arena.allocate(16L); + + if ( + // @start region="arena-overlap": + S1.asOverlappingSlice(S2).isEmpty() == true + // @end + ) {} + + } + } + } + + static class AddressLayoutSnippets { + void withTargetLayout() { + AddressLayout addressLayout = ADDRESS; + AddressLayout unboundedLayout = addressLayout.withTargetLayout( + sequenceLayout(ValueLayout.JAVA_BYTE)); + } + } + + static class FunctionDescriptionSnippets { + } + + static class GroupLayoutSnippets { + } + + static class LinkerSnippets { + + void downcall() throws Throwable { + Linker linker = Linker.nativeLinker(); + MethodHandle strlen = linker.downcallHandle( + linker.defaultLookup().find("strlen").orElseThrow(), + FunctionDescriptor.of(JAVA_LONG, ADDRESS) + ); + + try (Arena arena = Arena.ofConfined()) { + MemorySegment str = arena.allocateUtf8String("Hello"); + long len = (long) strlen.invokeExact(str); // 5 + } + + } + + void qsort() throws Throwable { + Linker linker = Linker.nativeLinker(); + MethodHandle qsort = linker.downcallHandle( + linker.defaultLookup().find("qsort").orElseThrow(), + FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS) + ); + + class Qsort { + static int qsortCompare(MemorySegment elem1, MemorySegment elem2) { + return Integer.compare(elem1.get(JAVA_INT, 0), elem2.get(JAVA_INT, 0)); + } + } + + FunctionDescriptor compareDesc = FunctionDescriptor.of(JAVA_INT, + ADDRESS.withTargetLayout(JAVA_INT), + ADDRESS.withTargetLayout(JAVA_INT)); + MethodHandle compareHandle = MethodHandles.lookup() + .findStatic(Qsort.class, "qsortCompare", + compareDesc.toMethodType()); + + + try (Arena arena = Arena.ofConfined()) { + MemorySegment compareFunc = linker.upcallStub(compareHandle, compareDesc, arena); + MemorySegment array = arena.allocateArray(JAVA_INT, 0, 9, 3, 4, 6, 5, 1, 8, 2, 7); + qsort.invokeExact(array, 10L, 4L, compareFunc); + int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + + } + } + + void returnPointer() throws Throwable { + Linker linker = Linker.nativeLinker(); + + MethodHandle malloc = linker.downcallHandle( + linker.defaultLookup().find("malloc").orElseThrow(), + FunctionDescriptor.of(ADDRESS, JAVA_LONG) + ); + + MethodHandle free = linker.downcallHandle( + linker.defaultLookup().find("free").orElseThrow(), + FunctionDescriptor.ofVoid(ADDRESS) + ); + + MemorySegment segment = (MemorySegment) malloc.invokeExact(100); + + class AllocateMemory { + + MemorySegment allocateMemory(long byteSize, Arena arena) throws Throwable { + MemorySegment segment = (MemorySegment) malloc.invokeExact(byteSize); // size = 0, scope = always alive + return segment.reinterpret(byteSize, arena, s -> { + try { + free.invokeExact(s); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); // size = byteSize, scope = arena.scope() + } + + } + + + class AllocateMemory2 { + + MemorySegment allocateMemory(long byteSize, Arena arena) { + MemorySegment segment = trySupplier(() -> (MemorySegment) malloc.invokeExact(byteSize)); // size = 0, scope = always alive + return segment.reinterpret(byteSize, arena, s -> trySupplier(() -> free.invokeExact(s))); // size = byteSize, scope = arena.scope() + } + + @FunctionalInterface + interface ThrowingSupplier { + T get() throws Throwable; + + } + + T trySupplier(ThrowingSupplier supplier) { + try { + return supplier.get(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + + } + + + class AllocateMemory3 { + + MemorySegment allocateMemory(long byteSize, Arena arena) throws Throwable { + MemorySegment segment = (MemorySegment) malloc.invokeExact(byteSize); // size = 0, scope = always alive + return segment.reinterpret(byteSize, arena, this::freeMemory); // size = byteSize, scope = arena.scope() + } + + void freeMemory(MemorySegment segment) { + try { + free.invokeExact(segment); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + } + + } + + void variadicFunc() throws Throwable { + + Linker linker = Linker.nativeLinker(); + MethodHandle printf = linker.downcallHandle( + linker.defaultLookup().find("printf").orElseThrow(), + FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT, JAVA_INT), + Linker.Option.firstVariadicArg(1) // first int is variadic + ); + + try (Arena arena = Arena.ofConfined()) { + int res = (int) printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4" + } + + } + + void downcallHandle() { + Linker linker = Linker.nativeLinker(); + FunctionDescriptor function = null; + MemorySegment symbol = null; + + linker.downcallHandle(function).bindTo(symbol); + + } + + void captureCallState() throws Throwable { + + MemorySegment targetAddress = null; // ... + Linker.Option ccs = Linker.Option.captureCallState("errno"); + MethodHandle handle = Linker.nativeLinker().downcallHandle(targetAddress, FunctionDescriptor.ofVoid(), ccs); + + StructLayout capturedStateLayout = Linker.Option.captureStateLayout(); + VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno")); + try (Arena arena = Arena.ofConfined()) { + MemorySegment capturedState = arena.allocate(capturedStateLayout); + handle.invoke(capturedState); + int errno = (int) errnoHandle.get(capturedState); + // use errno + } + } + + void captureStateLayout() { + String capturedNames = Linker.Option.captureStateLayout().memberLayouts().stream() + .map(MemoryLayout::name) + .flatMap(Optional::stream) + .map(Objects::toString) + .collect(Collectors.joining(", ")); + } + + + } + + static class MemoryLayoutSnippets { + + void header() throws Throwable { + SequenceLayout taggedValues = sequenceLayout(5, + structLayout( + ValueLayout.JAVA_BYTE.withName("kind"), + MemoryLayout.paddingLayout(24), + ValueLayout.JAVA_INT.withName("value") + ) + ).withName("TaggedValues"); + + long valueOffset = taggedValues.byteOffset(PathElement.sequenceElement(0), + PathElement.groupElement("value")); // yields 4 + + MemoryLayout value = taggedValues.select(PathElement.sequenceElement(), + PathElement.groupElement("value")); + + VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(), + PathElement.groupElement("value")); + + MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(), + PathElement.groupElement("kind")); + long offset1 = (long) offsetHandle.invokeExact(1L); // 8 + long offset2 = (long) offsetHandle.invokeExact(2L); // 16 + } + + void sliceHandle() { + MemorySegment segment = null; + long offset = 0; + MemoryLayout layout = null; + + segment.asSlice(offset, layout.byteSize()); + } + + void sequenceLayout0() { + MemoryLayout elementLayout = JAVA_INT; + + sequenceLayout(Long.MAX_VALUE / elementLayout.byteSize(), elementLayout); + } + + void structLayout0() { + MemoryLayout elementLayout = JAVA_INT; + + structLayout(JAVA_SHORT, JAVA_INT); + structLayout(JAVA_SHORT, MemoryLayout.paddingLayout(16), JAVA_INT); + structLayout(JAVA_SHORT, JAVA_INT.withByteAlignment(2)); + } + + } + + static class MemorySegmentSnippets { + void header() throws NoSuchMethodException, IllegalAccessException { + + { + MemorySegment segment = null; // ... + int value = segment.get(ValueLayout.JAVA_INT, 0); + } + + { + MemorySegment segment = null; // ... + + int value = segment.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0); + } + + { + MemorySegment segment = null; // ... + + VarHandle intHandle = MethodHandles.memorySegmentViewVarHandle(ValueLayout.JAVA_INT); + MethodHandle multiplyExact = MethodHandles.lookup() + .findStatic(Math.class, "multiplyExact", + MethodType.methodType(long.class, long.class, long.class)); + intHandle = MethodHandles.filterCoordinates(intHandle, 1, + MethodHandles.insertArguments(multiplyExact, 0, 4L)); + int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 + } + + { + MemorySegment segment = null; // ... + + VarHandle intHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(); + int value = (int) intHandle.get(segment, 3L); // get int element at offset 3 * 4 = 12 + } + + { + Arena arena = Arena.ofConfined(); + MemorySegment segment = arena.allocate(100); + MemorySegment slice = segment.asSlice(50, 10); + slice.get(ValueLayout.JAVA_INT, 20); // Out of bounds! + arena.close(); + slice.get(ValueLayout.JAVA_INT, 0); // Already closed! + } + + + { + try (Arena arena = Arena.ofShared()) { + SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT); + MemorySegment segment = arena.allocate(SEQUENCE_LAYOUT); + int sum = segment.elements(ValueLayout.JAVA_INT).parallel() + .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0)) + .sum(); + } + } + + { + MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]); + byteSegment.get(ValueLayout.JAVA_INT, 0); // fails: layout alignment is 4, segment max alignment is 1 + } + + { + MemorySegment longSegment = MemorySegment.ofArray(new long[10]); + longSegment.get(ValueLayout.JAVA_INT, 0); // ok: layout alignment is 4, segment max alignment is 8 + } + + { + MemorySegment byteSegment = MemorySegment.ofArray(new byte[10]); + byteSegment.get(ValueLayout.JAVA_INT_UNALIGNED, 0); // ok: layout alignment is 1, segment max alignment is 1 + } + + { + MemorySegment segment = null; + long offset = 42; + + MemorySegment z = segment.get(ValueLayout.ADDRESS, offset); // size = 0 + MemorySegment ptr = z.reinterpret(16); // size = 16 + int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // ok + } + + { + MemorySegment segment = null; + long offset = 42; + + MemorySegment ptr = null; + try (Arena arena = Arena.ofConfined()) { + MemorySegment z = segment.get(ValueLayout.ADDRESS, offset); // size = 0, scope = always alive + ptr = z.reinterpret(16, arena, null); // size = 4, scope = arena.scope() + int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // ok + } + int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // throws IllegalStateException + } + + { + MemorySegment segment = null; + long offset = 42; + + AddressLayout intArrPtrLayout = ValueLayout.ADDRESS.withTargetLayout( + MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT)); // layout for int (*ptr)[4] + MemorySegment ptr = segment.get(intArrPtrLayout, offset); // size = 16 + int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // ok + } + + + } + + boolean isAligned(MemorySegment segment, long offset, MemoryLayout layout) { + return ((segment.address() + offset) % layout.byteAlignment()) == 0; + } + + void elements() { + MemorySegment segment = null; + MemoryLayout elementLayout = JAVA_INT; + + StreamSupport.stream(segment.spliterator(elementLayout), false); + } + + void asSlice() { + MemorySegment segment = null; + long offset = 42; + MemoryLayout layout = JAVA_INT; + + segment.asSlice(offset, layout.byteSize(), 1); + + segment.asSlice(offset, layout.byteSize(), layout.byteAlignment()); + + segment.asSlice(offset, segment.byteSize() - offset); + + } + + void reinterpret() { + MemorySegment segment = null; + + MemorySegment cleanupSegment = MemorySegment.ofAddress(segment.address()); + + } + + void segmentOffset() { + MemorySegment segment = null; + MemorySegment other = null; + + long offset = other.address() - segment.address(); + } + + void fill() { + MemorySegment segment = null; + byte value = 42; + + var byteHandle = MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE) + .varHandle(MemoryLayout.PathElement.sequenceElement()); + for (long l = 0; l < segment.byteSize(); l++) { + byteHandle.set(segment.address(), l, value); + } + } + + void copyFrom() { + MemorySegment src = null; + MemorySegment dst = null; + + // MemorySegment.copy(src, 0, this, 0, src.byteSize()); + MemorySegment.copy(src, 0, dst, 0, src.byteSize()); + } + + void copy() { + MemorySegment srcSegment = null; + long srcOffset = 42; + MemorySegment dstSegment = null; + long dstOffset = 13; + long bytes = 3; + + MemorySegment.copy(srcSegment, ValueLayout.JAVA_BYTE, srcOffset, dstSegment, ValueLayout.JAVA_BYTE, dstOffset, bytes); + } + + + } + + static class PackageInfoSnippets { + + void header() throws Throwable { + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(10 * 4); + for (int i = 0; i < 10; i++) { + segment.setAtIndex(ValueLayout.JAVA_INT, i, i); + } + } + + Linker linker = Linker.nativeLinker(); + SymbolLookup stdlib = linker.defaultLookup(); + MethodHandle strlen = linker.downcallHandle( + stdlib.find("strlen").orElseThrow(), + FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS) + ); + + try (Arena arena = Arena.ofConfined()) { + MemorySegment cString = arena.allocateUtf8String("Hello"); + long len = (long) strlen.invokeExact(cString); // 5 + } + + } + } + + static class PaddingLayoutSnippets { + } + + static class SegmentAllocatorSnippets { + void prefixAllocator() { + MemorySegment segment = null; //... + SegmentAllocator prefixAllocator = (size, align) -> segment.asSlice(0, size); + } + + } + + static class SequenceLayoutSnippets { + void header() { + MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)); + + MemoryLayout.structLayout( + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), + ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)); + + } + + void reshape() { + var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT)); + var reshapeSeq = MemoryLayout.sequenceLayout(2, MemoryLayout.sequenceLayout(6, ValueLayout.JAVA_INT)); + + var reshapeSeqImplicit1 = seq.reshape(-1, 6); + var reshapeSeqImplicit2 = seq.reshape(2, -1); + + } + + void flatten() { + var seq = MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_INT)); + var flattenedSeq = MemoryLayout.sequenceLayout(12, ValueLayout.JAVA_INT); + } + + } + + static class StructLayoutSnippets { + } + + static class SymbolLookupSnippets { + + void header() { + try (Arena arena = Arena.ofConfined()) { + SymbolLookup libGL = libraryLookup("libGL.so", arena); // libGL.so loaded here + MemorySegment glGetString = libGL.find("glGetString").orElseThrow(); + // ... + } // libGL.so unloaded here + + System.loadLibrary("GL"); // libGL.so loaded here + // ... + SymbolLookup libGL = loaderLookup(); + MemorySegment glGetString = libGL.find("glGetString").orElseThrow(); + + + Arena arena = Arena.ofAuto(); + + + libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true + loaderLookup().find("glGetString").isPresent(); // false + + libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true + loaderLookup().find("glGetString").isPresent(); // false + + Linker nativeLinker = Linker.nativeLinker(); + SymbolLookup stdlib = nativeLinker.defaultLookup(); + MemorySegment malloc = stdlib.find("malloc").orElseThrow(); + } + + } + + static class UnionLayoutSnippets { + } + + static class ValueLayoutSnippets { + + void arrayElementVarHandle() { + VarHandle arrayHandle = ValueLayout.JAVA_INT.arrayElementVarHandle(10, 20); + + SequenceLayout arrayLayout = MemoryLayout.sequenceLayout( + MemoryLayout.sequenceLayout(10, + MemoryLayout.sequenceLayout(20, ValueLayout.JAVA_INT))); + + int value1 = (int) arrayHandle.get(10, 2, 4); // ok, accessed offset = 8176 + int value2 = (int) arrayHandle.get(0, 0, 30); // out of bounds value for z + } + + void statics() { + ADDRESS.withByteAlignment(1); + JAVA_CHAR.withByteAlignment(1); + JAVA_SHORT.withByteAlignment(1); + JAVA_INT.withByteAlignment(1); + JAVA_LONG.withByteAlignment(1); + JAVA_FLOAT.withByteAlignment(1); + JAVA_DOUBLE.withByteAlignment(1); + } + + } + +}