Summary
Add methods to MemoryLayout
that can be used create method handles that lazily compute offsets of nested layouts, by specifying a set of element indices corresponding to one or more enclosing sequence layouts.
Problem
MemoryLayout::byteOffset
is useful, but only works when the path elements passed to it are fully specified.
There might be cases where the user has a multi-dimensional layout like:
MemoryLayout.ofSequence(100,
MemoryLayout.ofSequence(200,
MemoryLayout.ofValueBits(32).withName("elem")
)
)
Now, when we derive a VarHandle
for this, we can get two free variables, for the two sequence indices - these values will be used to determine the final offset calculation.
But there's no way to do this with offsets - e.g. there's no way to pass an open-ended layout path to MemoryLayout::byteOffset
- because that method wants the layout to be fully specified.
Solution
To improve usability in this cases, we add two extra methods:
MethodHandle MemoryLayout::bitOffsetHandle(PathElement...)
MethodHandle MemoryLayout::byteOffsetHandle
Which returns a MethodHandle whose signature is:
(J...J)J
where the long
parameters correspond to one or more free dimensions in the layout path. This would add some symmetry to the API, since now both VarHandle
s and offsets can be computed in the same way.
The patch also removes an incorrect apiNote in the javadoc of the bitOffset
, and byteOffset
methods. This states that free dimensions in the layout path will be interpreted as having an index of 0
, but actually the implementation rejects these outright.
Specification
All spec changes are made to the jdk.incubator.foreign.MemoryLayout class.
In the class' javadoc:
* it follows that the memory access var handle {@code valueHandle} will feature an <em>additional</em> {@code long}
* access coordinate.
*
+ * <p>A layout path with free dimensions can also be used to create an offset computing method handle, using the
+ * {@link #bitOffset(PathElement...)} or {@link #byteOffsetHandle(PathElement...)} method. Again, free dimensions are
+ * translated into {@code long} parameters of the created method handle. The method handle can be used to compute the
+ * offsets of elements of a sequence at different indices, by supplying these indices when invoking the method handle.
+ * For instance:
+ *
+ * <blockquote><pre>{@code
+MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(),
+ PathElement.groupElement("kind"));
+long offset1 = (long) offsetHandle.invokeExact(1L); // 8
+long offset2 = (long) offsetHandle.invokeExact(2L); // 16
+ * }</pre></blockquote>
+ *
* <h2>Layout attributes</h2>
*
* Layouts can be optionally associated with one or more <em>attributes</em>. A layout attribute forms a <em>name/value</em>
...
* Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this
* layout.
*
- * @apiNote if the layout path has one (or more) free dimensions,
- * the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
- *
* @param elements the layout path elements.
* @return The offset, in bits, of the layout selected by the layout path in {@code elements}.
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
* layout path contains one or more path elements that select multiple sequence element indices
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
* @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
*/
default long bitOffset(PathElement... elements) {
...
+ /**
+ * Creates a method handle that can be used to compute the offset, in bits, of the layout selected
+ * by a given layout path, where the path is considered rooted in this layout.
+ *
+ * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
+ * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()},
+ * where the order of the parameters corresponds to the order of the path elements.
+ * The returned method handle can be used to compute a layout offset similar to {@link #bitOffset(PathElement...)},
+ * but where some sequence indices are specified only when invoking the method handle.
+ *
+ * <p>The final offset returned by the method handle is computed as follows:
+ *
+ * <blockquote><pre>{@code
+ offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
+ * }</pre></blockquote>
+ *
+ * where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as {@code long}
+ * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
+ * <em>static</em> stride constants which are derived from the layout path.
+ *
+ * @param elements the layout path elements.
+ * @return a method handle that can be used to compute the bit offset of the layout element
+ * specified by the given layout path elements, when supplied with the missing sequence element indices.
+ * @throws IllegalArgumentException if the layout path contains one or more path elements that select
+ * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
+ * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
+ */
+ default MethodHandle bitOffsetHandle(PathElement... elements) {
...
/**
* Computes the offset, in bytes, of the layout selected by a given layout path, where the path is considered rooted in this
* layout.
*
- * @apiNote if the layout path has one (or more) free dimensions,
- * the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
- *
* @param elements the layout path elements.
* @return The offset, in bytes, of the layout selected by the layout path in {@code elements}.
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
* layout path contains one or more path elements that select multiple sequence element indices
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
* @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size,
* or if {@code bitOffset(elements)} is not a multiple of 8.
*/
default long byteOffset(PathElement... elements) {
...
+ /**
+ * Creates a method handle that can be used to compute the offset, in bytes, of the layout selected
+ * by a given layout path, where the path is considered rooted in this layout.
+ *
+ * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
+ * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()},
+ * where the order of the parameters corresponds to the order of the path elements.
+ * The returned method handle can be used to compute a layout offset similar to {@link #byteOffset(PathElement...)},
+ * but where some sequence indices are specified only when invoking the method handle.
+ *
+ * <p>The final offset returned by the method handle is computed as follows:
+ *
+ * <blockquote><pre>{@code
+ bitOffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
+ offset = bitOffset / 8
+ * }</pre></blockquote>
+ *
+ * where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as {@code long}
+ * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
+ * <em>static</em> stride constants which are derived from the layout path.
+ *
+ * <p>The method handle will throw an {@link UnsupportedOperationException} if the computed
+ * offset in bits is not a multiple of 8.
+ *
+ * @param elements the layout path elements.
+ * @return a method handle that can be used to compute the byte offset of the layout element
+ * specified by the given layout path elements, when supplied with the missing sequence element indices.
+ * @throws IllegalArgumentException if the layout path contains one or more path elements that select
+ * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
+ * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
+ */
+ default MethodHandle byteOffsetHandle(PathElement... elements) {
- csr of
-
JDK-8257184 Upstream 8252504: Add a method to MemoryLayout which returns a offset-computing method handle
-
- Resolved
-