8317824: Beef up javadoc for base offset in var handles derived from layouts (mainline)

Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org>
Reviewed-by: mcimadamore
This commit is contained in:
Jorn Vernee 2023-10-13 19:06:07 +00:00
parent b12c471a99
commit 32a60cf11d
2 changed files with 105 additions and 44 deletions
src/java.base/share/classes/java/lang/foreign

@ -77,7 +77,7 @@ import jdk.internal.vm.annotation.ForceInline;
* The above declaration can be modelled using a layout object, as follows:
*
* {@snippet lang=java :
* SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5,
* SequenceLayout TAGGED_VALUES = MemoryLayout.sequenceLayout(5,
* MemoryLayout.structLayout(
* ValueLayout.JAVA_BYTE.withName("kind"),
* MemoryLayout.paddingLayout(3),
@ -132,13 +132,13 @@ import jdk.internal.vm.annotation.ForceInline;
* For instance, given the {@code taggedValues} sequence layout constructed above, we can obtain the offset,
* in bytes, of the member layout named <code>value</code> in the <em>first</em> sequence element, as follows:
* {@snippet lang=java :
* long valueOffset = taggedValues.byteOffset(PathElement.sequenceElement(0),
* long valueOffset = TAGGED_VALUES.byteOffset(PathElement.sequenceElement(0),
* PathElement.groupElement("value")); // yields 4
* }
*
* Similarly, we can select the member layout named {@code value}, as follows:
* {@snippet lang=java :
* MemoryLayout value = taggedValues.select(PathElement.sequenceElement(),
* MemoryLayout value = TAGGED_VALUES.select(PathElement.sequenceElement(),
* PathElement.groupElement("value"));
* }
*
@ -151,10 +151,13 @@ import jdk.internal.vm.annotation.ForceInline;
* the open elements in the path:
*
* {@snippet lang=java :
* VarHandle valueHandle = taggedValues.varHandle(PathElement.sequenceElement(),
* VarHandle valueHandle = TAGGED_VALUES.varHandle(PathElement.sequenceElement(),
* PathElement.groupElement("value"));
* MemorySegment valuesSegment = ...
* int val = (int) valueHandle.get(valuesSegment, 2); // reads the "value" field of the third struct in the array
* MemorySegment taggedValues = ...
* // reads the "value" field of the third struct in the array (taggedValues[2].value)
* int val = (int) valueHandle.get(taggedValues,
* 0L, // base offset
* 2L); // sequence index
* }
*
* <p>
@ -164,10 +167,10 @@ import jdk.internal.vm.annotation.ForceInline;
* of the sequence element whose offset is to be computed:
*
* {@snippet lang=java :
* MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(),
* MethodHandle offsetHandle = TAGGED_VALUES.byteOffsetHandle(PathElement.sequenceElement(),
* PathElement.groupElement("kind"));
* long offset1 = (long) offsetHandle.invokeExact(1L); // 8
* long offset2 = (long) offsetHandle.invokeExact(2L); // 16
* long offset1 = (long) offsetHandle.invokeExact(0L, 1L); // 0 + (1 * 8) = 8
* long offset2 = (long) offsetHandle.invokeExact(0L, 2L); // 0 + (2 * 8) = 16
* }
*
* <h3 id="deref-path-elements">Dereference path elements</h3>
@ -205,7 +208,10 @@ import jdk.internal.vm.annotation.ForceInline;
* );
*
* MemorySegment rect = ...
* int rect_y_4 = (int) rectPointYs.get(rect, 2); // rect.points[2]->y
* // dereferences the third point struct in the "points" array, and reads its "y" coordinate (rect.points[2]->y)
* int rect_y_2 = (int) rectPointYs.get(rect,
* 0L, // base offset
* 2L); // sequence index
* }
*
* <h3 id="well-formedness">Layout path well-formedness</h3>
@ -269,6 +275,67 @@ import jdk.internal.vm.annotation.ForceInline;
* access modes. All other access modes will result in {@link UnsupportedOperationException} being thrown. Moreover,
* while supported, access modes {@code get} and {@code set} might lead to word tearing.
*
* <h2 id="variable-length">Working with variable-length structs</h2>
*
* Memory layouts allow clients to describe the contents of a region of memory whose size is known <em>statically</em>.
* There are, however, cases, where the size of a region of memory is only known <em>dynamically</em>, as it depends
* on the value of one or more struct fields. Consider the following struct declaration in C:
*
* {@snippet lang=c :
* typedef struct {
* int size;
* struct {
* int x;
* int y;
* } points[];
* } Polygon;
* }
*
* In the above code, a polygon is modelled as a size (the number of edges in the polygon) and an array of points
* (one for each vertex in the polygon). The number of vertices depends on the number of edges in the polygon. As such,
* the size of the {@code points} array is left <em>unspecified</em> in the C declaration, using a <em>Flexible Array Member</em>
* (a feature standardized in C99).
* <p>
* Memory layouts do not support sequence layouts whose size is unknown. As such, it is not possible to model
* the above struct directly. That said, clients can still enjoy structured access provided by memory layouts, as
* demonstrated below:
*
* {@snippet lang=java :
* StructLayout POINT = MemoryLayout.structLayout(
* ValueLayout.JAVA_INT.withName("x"),
* ValueLayout.JAVA_INT.withName("y")
* );
*
* StructLayout POLYGON = MemoryLayout.structLayout(
* ValueLayout.JAVA_INT.withName("size"),
* MemoryLayout.sequenceLayout(0, POINT).withName("points")
* );
*
* VarHandle POLYGON_SIZE = POLYGON.varHandle(0, PathElement.groupElement("size"));
* VarHandle POINT_X = POINT.varHandle(PathElement.groupElement("x"));
* long POINTS_OFFSET = POLYGON.byteOffset(PathElement.groupElement("points"));
* }
*
* Note how we have split the polygon struct in two. The {@code POLYGON} layout contains a sequence layout
* of size <em>zero</em>. The element layout of the sequence layout is the {@code POINT} layout, which defines
* the {@code x} and {@code y} coordinates, accordingly. The first layout is used to obtain a var handle
* that provides access to the polygon size; the second layout is used to obtain a var handle that provides
* access to the {@code x} coordinate of a point struct. Finally, an offset to the start of the variable-length
* {@code points} array is also obtained.
* <p>
* The {@code x} coordinates of all the points in a polygon can then be accessed as follows:
* {@snippet lang=java :
* MemorySegment polygon = ...
* int size = POLYGON_SIZE.get(polygon, 0L);
* for (int i = 0 ; i < size ; i++) {
* int x = POINT_X.get(polygon, POINT.scaleOffset(POINTS_OFFSET, i));
* }
* }
* Here, we first obtain the polygon size, using the {@code POLYGON_SIZE} var handle. Then, in a loop, we read
* the {@code x} coordinates of all the points in the polygon. This is done by providing a custom base offset to
* the {@code POINT_X} var handle. The custom offset is computed as {@code POINTS_OFFSET + (i * POINT.byteSize())}, where
* {@code i} is the loop induction variable.
*
* @implSpec
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*

@ -26,6 +26,9 @@
package java.lang.foreign;
import java.io.UncheckedIOException;
import java.lang.foreign.ValueLayout.OfInt;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -120,51 +123,42 @@ import jdk.internal.vm.annotation.ForceInline;
* If the value to be read is stored in memory using {@linkplain ByteOrder#BIG_ENDIAN big-endian} encoding, the access operation
* can be expressed as follows:
* {@snippet lang=java :
* MemorySegment segment = ...
* int value = segment.get(ValueLayout.JAVA_INT.withOrder(BIG_ENDIAN), 0);
* }
*
* More complex access operations can be implemented using var handles. The {@link ValueLayout#varHandle()}
* method can be used to obtain a var handle that can be used to get/set values represented by the given value layout on a memory segment.
* A var handle obtained from a layout supports several additional <a href=MemoryLayout.html#access-mode-restrictions>
* access modes</a>. More importantly, var handles can be <em>combined</em> with method handles to express complex access
* operations. For instance, a var handle that can be used to access an element of an {@code int} array at a given logical
* Access operations on memory segments are implemented using var handles. The {@link ValueLayout#varHandle()}
* method can be used to obtain a var handle that can be used to get/set values represented by the given value layout
* on a memory segment at the given offset:
*
* {@snippet lang=java:
* VarHandle intAtOffsetHandle = ValueLayout.JAVA_INT.varHandle(); // (MemorySegment, long)
* int value = (int) intAtOffsetHandle.get(segment, 10L); // segment.get(ValueLayout.JAVA_INT, 10L)
* }
*
* The var handle returned by {@link ValueLayout#varHandle()} features a <em>base offset</em> parameter. This parameter
* allows clients to express complex access operations, by injecting additional offset computation into the var handle.
* For instance, a var handle that can be used to access an element of an {@code int} array at a given logical
* index can be created as follows:
*
* {@snippet lang=java:
* MemorySegment segment = ...
* VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); // (MemorySegment, long)
* MethodHandle scale = ValueLayout.JAVA_INT.scaleHandle(); // <base offset> + <index> * JAVA_INT.byteSize()
*
* intHandle = MethodHandles.filterCoordinates(intHandle, 1, scale);
* int value = (int) intHandle.get(segment, 0L, 3L); // get int element at offset 0 + 3 * 4 = 12
* MethodHandle scale = ValueLayout.JAVA_INT.scaleHandle(); // (long, long)long
* VarHandle intAtOffsetAndIndexHandle =
* MethodHandles.collectCoordinates(intAtOffsetHandle, 1, scale); // (MemorySegment, long, long)
* int value = (int) intAtOffsetAndIndexHandle.get(segment, 2L, 3L); // segment.get(ValueLayout.JAVA_INT, 2L + (3L * 4L))
* }
*
* To make the process of creating these var handles easier, the method
* {@link MemoryLayout#varHandle(MemoryLayout.PathElement...)} can be used, by providing it a so called
* <a href="MemoryLayout.html#layout-paths"><em>layout path</em></a>. A layout path, consisting of several <em>layout
* path elements</em>, selects a value layout to be accessed, which can be nested inside another memory layout. For example,
* we can express the access to an element of an {@code int} array using layout paths like so:
* <p>
* Clients can also drop the base offset parameter, in order to make the access expression simpler. This can be used to
* implement access operation such as {@link #getAtIndex(OfInt, long)}:
*
* {@snippet lang=java :
* MemorySegment segment = ...
* MemoryLayout segmentLayout = MemoryLayout.structLayout(
* ValueLayout.JAVA_INT.withName("size"),
* MemoryLayout.sequenceLayout(4, ValueLayout.JAVA_INT).withName("data") // array of 4 elements
* );
* VarHandle intHandle = segmentLayout.varHandle(MemoryLayout.PathElement.groupElement("data"),
* MemoryLayout.PathElement.sequenceElement());
* int value = (int) intHandle.get(segment, 0L, 3L); // get int element at offset 0 + offsetof(data) + 3 * 4 = 12
* {@snippet lang=java:
* VarHandle intAtIndexHandle =
* MethodHandles.insertCoordinates(intAtOffsetAndIndexHandle, 1, 0L); // (MemorySegment, long)
* int value = (int) intAtIndexHandle.get(segment, 3L); // segment.getAtIndex(ValueLayout.JAVA_INT, 3L);
* }
* Where {@code offsetof(data)} is the offset of the {@code data} element layout of the {@code segmentLayout} layout
*
* Both the var handle returned by {@link ValueLayout#varHandle()} and
* {@link MemoryLayout#varHandle(MemoryLayout.PathElement...)}, as well as the method handle returned by
* {@link MemoryLayout#byteOffsetHandle(MemoryLayout.PathElement...)} and {@link MemoryLayout#sliceHandle(MemoryLayout.PathElement...)}
* feature a <em>base offset</em> parameter. This parameter represents a base offset for the offset computation. This
* parameter allows a client to combine these handles further with additional offset computations. This is demonstrated
* in the first of the two examples above, where {@code intHandle} is combined with a
* {@linkplain MemoryLayout#scaleHandle() scale handle} obtained from {@code ValueLayout.JAVA_INT}.
* Var handles for more complex access expressions (e.g. struct field access, pointer dereference) can be created directly
* from memory layouts, using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
*
* <h2 id="slicing">Slicing memory segments</h2>
*