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:
parent
b12c471a99
commit
32a60cf11d
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>
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user