8257184: Upstream 8252504: Add a method to MemoryLayout which returns a offset-computing method handle

Reviewed-by: mcimadamore, chegar
This commit is contained in:
Jorn Vernee 2020-12-07 11:08:24 +00:00
parent 5a03e47605
commit 04ce8e3840
4 changed files with 314 additions and 57 deletions

View File

@ -31,10 +31,11 @@ import jdk.internal.foreign.Utils;
import java.lang.constant.Constable;
import java.lang.constant.DynamicConstantDesc;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
@ -173,6 +174,19 @@ VarHandle valueHandle = taggedValues.varHandle(int.class,
* 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>
@ -337,9 +351,6 @@ public interface MemoryLayout extends Constable {
* 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
@ -348,16 +359,46 @@ public interface MemoryLayout extends Constable {
* @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
*/
default long bitOffset(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offset, EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements);
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offset,
EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), 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) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offsetHandle,
EnumSet.of(PathKind.SEQUENCE_RANGE), 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
@ -367,8 +408,44 @@ public interface MemoryLayout extends Constable {
* or if {@code bitOffset(elements)} is not a multiple of 8.
*/
default long byteOffset(PathElement... elements) {
return Utils.bitsToBytesOrThrow(bitOffset(elements),
() -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8"));
return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.bitsToBytesThrowOffset);
}
/**
* 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) {
MethodHandle mh = bitOffsetHandle(elements);
mh = MethodHandles.filterReturnValue(mh, Utils.MH_bitsToBytesOrThrowForOffset);
return mh;
}
/**

View File

@ -61,11 +61,17 @@ public class LayoutPath {
private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private static final MethodHandle ADD_STRIDE;
private static final MethodHandle MH_ADD_SCALED_OFFSET;
private static final int UNSPECIFIED_ELEM_INDEX = -1;
static {
try {
ADD_STRIDE = MethodHandles.lookup().findStatic(LayoutPath.class, "addStride",
MethodHandles.Lookup lookup = MethodHandles.lookup();
ADD_STRIDE = lookup.findStatic(LayoutPath.class, "addStride",
MethodType.methodType(long.class, MemorySegment.class, long.class, long.class, long.class));
MH_ADD_SCALED_OFFSET = lookup.findStatic(LayoutPath.class, "addScaledOffset",
MethodType.methodType(long.class, long.class, long.class, long.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
@ -93,7 +99,7 @@ public class LayoutPath {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
MemoryLayout elem = seq.elementLayout();
return LayoutPath.nestedPath(elem, offset, addStride(sizeFunc.applyAsLong(elem)), -1, this);
return LayoutPath.nestedPath(elem, offset, addStride(sizeFunc.applyAsLong(elem)), UNSPECIFIED_ELEM_INDEX, this);
}
public LayoutPath sequenceElement(long start, long step) {
@ -102,7 +108,8 @@ public class LayoutPath {
checkSequenceBounds(seq, start);
MemoryLayout elem = seq.elementLayout();
long elemSize = sizeFunc.applyAsLong(elem);
return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), -1, this);
return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step),
UNSPECIFIED_ELEM_INDEX, this);
}
public LayoutPath sequenceElement(long index) {
@ -177,6 +184,22 @@ public class LayoutPath {
return handle;
}
private static long addScaledOffset(long base, long index, long stride) {
return base + (stride * index);
}
public MethodHandle offsetHandle() {
MethodHandle mh = MethodHandles.identity(long.class);
for (int i = strides.length - 1; i >=0; i--) {
MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, strides[i]);
// (J, ...) -> J to (J, J, ...) -> J
// i.e. new coord is prefixed. Last coord will correspond to innermost layout
mh = MethodHandles.collectArguments(mh, 0, collector);
}
mh = MethodHandles.insertArguments(mh, 0, offset);
return mh;
}
public MemoryLayout layout() {
return layout;
}

View File

@ -53,11 +53,21 @@ public final class Utils {
.orElse("deny");
private static final MethodHandle SEGMENT_FILTER;
public static final MethodHandle MH_bitsToBytesOrThrowForOffset;
public static final Supplier<RuntimeException> bitsToBytesThrowOffset
= () -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8");
static {
try {
SEGMENT_FILTER = MethodHandles.lookup().findStatic(Utils.class, "filterSegment",
MethodHandles.Lookup lookup = MethodHandles.lookup();
SEGMENT_FILTER = lookup.findStatic(Utils.class, "filterSegment",
MethodType.methodType(MemorySegmentProxy.class, MemorySegment.class));
MH_bitsToBytesOrThrowForOffset = MethodHandles.insertArguments(
lookup.findStatic(Utils.class, "bitsToBytesOrThrow",
MethodType.methodType(long.class, long.class, Supplier.class)),
1,
bitsToBytesThrowOffset);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}

View File

@ -35,127 +35,182 @@ import jdk.incubator.foreign.SequenceLayout;
import org.testng.annotations.*;
import java.lang.invoke.MethodHandle;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
import static org.testng.Assert.*;
public class TestLayoutPaths {
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadBitSelectFromSeq() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
SequenceLayout seq = MemoryLayout.ofSequence(JAVA_INT);
seq.bitOffset(PathElement.groupElement("foo"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadByteSelectFromSeq() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
SequenceLayout seq = MemoryLayout.ofSequence(JAVA_INT);
seq.byteOffset(PathElement.groupElement("foo"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadBitSelectFromStruct() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.bitOffset(PathElement.sequenceElement());
GroupLayout g = MemoryLayout.ofStruct(JAVA_INT);
g.bitOffset(sequenceElement());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadByteSelectFromStruct() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.byteOffset(PathElement.sequenceElement());
GroupLayout g = MemoryLayout.ofStruct(JAVA_INT);
g.byteOffset(sequenceElement());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadBitSelectFromValue() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
SequenceLayout seq = MemoryLayout.ofSequence(JAVA_INT);
seq.bitOffset(sequenceElement(), sequenceElement());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadByteSelectFromValue() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.byteOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
SequenceLayout seq = MemoryLayout.ofSequence(JAVA_INT);
seq.byteOffset(sequenceElement(), sequenceElement());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnknownBitStructField() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
GroupLayout g = MemoryLayout.ofStruct(JAVA_INT);
g.bitOffset(PathElement.groupElement("foo"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnknownByteStructField() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
GroupLayout g = MemoryLayout.ofStruct(JAVA_INT);
g.byteOffset(PathElement.groupElement("foo"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitOutOfBoundsSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(6));
SequenceLayout seq = MemoryLayout.ofSequence(5, JAVA_INT);
seq.bitOffset(sequenceElement(6));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteOutOfBoundsSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.byteOffset(PathElement.sequenceElement(6));
SequenceLayout seq = MemoryLayout.ofSequence(5, JAVA_INT);
seq.byteOffset(sequenceElement(6));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqIndex() {
PathElement.sequenceElement(-2);
sequenceElement(-2);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitNegativeSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(-2));
SequenceLayout seq = MemoryLayout.ofSequence(5, JAVA_INT);
seq.bitOffset(sequenceElement(-2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteNegativeSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.byteOffset(PathElement.sequenceElement(-2));
SequenceLayout seq = MemoryLayout.ofSequence(5, JAVA_INT);
seq.byteOffset(sequenceElement(-2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOutOfBoundsSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(6, 2));
SequenceLayout seq = MemoryLayout.ofSequence(5, JAVA_INT);
seq.bitOffset(sequenceElement(6, 2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqRange() {
PathElement.sequenceElement(-2, 2);
sequenceElement(-2, 2);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitNegativeSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(-2, 2));
SequenceLayout seq = MemoryLayout.ofSequence(5, JAVA_INT);
seq.bitOffset(sequenceElement(-2, 2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteNegativeSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.byteOffset(PathElement.sequenceElement(-2, 2));
SequenceLayout seq = MemoryLayout.ofSequence(5, JAVA_INT);
seq.byteOffset(sequenceElement(-2, 2));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testIncompleteAccess() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT));
seq.varHandle(int.class, PathElement.sequenceElement());
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayout.ofStruct(JAVA_INT));
seq.varHandle(int.class, sequenceElement());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitOffsetHandleBadRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayout.ofStruct(JAVA_INT));
seq.bitOffsetHandle(sequenceElement(0, 1)); // ranges not accepted
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteOffsetHandleBadRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayout.ofStruct(JAVA_INT));
seq.byteOffsetHandle(sequenceElement(0, 1)); // ranges not accepted
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testBadMultiple() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(3), MemoryLayouts.JAVA_INT.withName("foo"));
GroupLayout g = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(3), JAVA_INT.withName("foo"));
g.byteOffset(PathElement.groupElement("foo"));
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testBitOffsetBadUnboundedSequenceTraverse() {
MemoryLayout layout = MemoryLayout.ofSequence(MemoryLayout.ofSequence(JAVA_INT));
layout.bitOffset(sequenceElement(1), sequenceElement(0));
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testByteOffsetBadUnboundedSequenceTraverse() {
MemoryLayout layout = MemoryLayout.ofSequence(MemoryLayout.ofSequence(JAVA_INT));
layout.byteOffset(sequenceElement(1), sequenceElement(0));
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testBitOffsetHandleBadUnboundedSequenceTraverse() {
MemoryLayout layout = MemoryLayout.ofSequence(MemoryLayout.ofSequence(JAVA_INT));
layout.bitOffsetHandle(sequenceElement(1), sequenceElement(0));
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testByteOffsetHandleBadUnboundedSequenceTraverse() {
MemoryLayout layout = MemoryLayout.ofSequence(MemoryLayout.ofSequence(JAVA_INT));
layout.byteOffsetHandle(sequenceElement(1), sequenceElement(0));
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testBadByteOffsetNoMultipleOf8() {
MemoryLayout layout = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(7), JAVA_INT.withName("x"));
layout.byteOffset(groupElement("x"));
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testBadByteOffsetHandleNoMultipleOf8() throws Throwable {
MemoryLayout layout = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(7), JAVA_INT.withName("x"));
MethodHandle handle = layout.byteOffsetHandle(groupElement("x"));
handle.invoke();
}
@Test
public void testBadContainerAlign() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
GroupLayout g = MemoryLayout.ofStruct(JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
try {
g.bitOffset(PathElement.groupElement("foo"));
g.byteOffset(PathElement.groupElement("foo"));
@ -174,7 +229,7 @@ public class TestLayoutPaths {
@Test
public void testBadAlignOffset() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo"));
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, JAVA_INT.withBitAlignment(16).withName("foo"));
try {
g.bitOffset(PathElement.groupElement("foo"));
g.byteOffset(PathElement.groupElement("foo"));
@ -193,9 +248,9 @@ public class TestLayoutPaths {
@Test
public void testBadSequencePathInOffset() {
SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
SequenceLayout seq = MemoryLayout.ofSequence(10, JAVA_INT);
// bad path elements
for (PathElement e : List.of( PathElement.sequenceElement(), PathElement.sequenceElement(0, 2) )) {
for (PathElement e : List.of( sequenceElement(), sequenceElement(0, 2) )) {
try {
seq.bitOffset(e);
fail();
@ -213,8 +268,8 @@ public class TestLayoutPaths {
@Test
public void testBadSequencePathInSelect() {
SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
for (PathElement e : List.of( PathElement.sequenceElement(0), PathElement.sequenceElement(0, 2) )) {
SequenceLayout seq = MemoryLayout.ofSequence(10, JAVA_INT);
for (PathElement e : List.of( sequenceElement(0), sequenceElement(0, 2) )) {
try {
seq.select(e);
fail();
@ -226,8 +281,8 @@ public class TestLayoutPaths {
@Test
public void testBadSequencePathInMap() {
SequenceLayout seq = MemoryLayout.ofSequence(10, MemoryLayouts.JAVA_INT);
for (PathElement e : List.of( PathElement.sequenceElement(0), PathElement.sequenceElement(0, 2) )) {
SequenceLayout seq = MemoryLayout.ofSequence(10, JAVA_INT);
for (PathElement e : List.of( sequenceElement(0), sequenceElement(0, 2) )) {
try {
seq.map(l -> l, e);
fail();
@ -326,22 +381,114 @@ public class TestLayoutPaths {
// test select
MemoryLayout selected = g.select(PathElement.sequenceElement());
MemoryLayout selected = g.select(sequenceElement());
assertTrue(selected == MemoryLayouts.JAVA_BYTE);
// test offset
for (int i = 0 ; i < 4 ; i++) {
long bitOffset = g.bitOffset(PathElement.sequenceElement(i));
long bitOffset = g.bitOffset(sequenceElement(i));
assertEquals(offsets[i], bitOffset);
long byteOffset = g.byteOffset(PathElement.sequenceElement(i));
long byteOffset = g.byteOffset(sequenceElement(i));
assertEquals((offsets[i]) >>> 3, byteOffset);
}
// test map
SequenceLayout seq2 = (SequenceLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, PathElement.sequenceElement());
SequenceLayout seq2 = (SequenceLayout)g.map(l -> MemoryLayouts.JAVA_DOUBLE, sequenceElement());
assertTrue(seq2.elementLayout() == MemoryLayouts.JAVA_DOUBLE);
}
@Test(dataProvider = "offsetHandleCases")
public void testOffsetHandle(MemoryLayout layout, PathElement[] pathElements, long[] indexes,
long expectedBitOffset) throws Throwable {
MethodHandle bitOffsetHandle = layout.bitOffsetHandle(pathElements);
bitOffsetHandle = bitOffsetHandle.asSpreader(long[].class, indexes.length);
long actualBitOffset = (long) bitOffsetHandle.invokeExact(indexes);
assertEquals(actualBitOffset, expectedBitOffset);
if (expectedBitOffset % 8 == 0) {
MethodHandle byteOffsetHandle = layout.byteOffsetHandle(pathElements);
byteOffsetHandle = byteOffsetHandle.asSpreader(long[].class, indexes.length);
long actualByteOffset = (long) byteOffsetHandle.invokeExact(indexes);
assertEquals(actualByteOffset, expectedBitOffset / 8);
}
}
@DataProvider
public static Object[][] offsetHandleCases() {
List<Object[]> testCases = new ArrayList<>();
testCases.add(new Object[] {
MemoryLayout.ofSequence(10, JAVA_INT),
new PathElement[] { sequenceElement() },
new long[] { 4 },
JAVA_INT.bitSize() * 4
});
testCases.add(new Object[] {
MemoryLayout.ofSequence(10, MemoryLayout.ofStruct(JAVA_INT, JAVA_INT.withName("y"))),
new PathElement[] { sequenceElement(), groupElement("y") },
new long[] { 4 },
(JAVA_INT.bitSize() * 2) * 4 + JAVA_INT.bitSize()
});
testCases.add(new Object[] {
MemoryLayout.ofSequence(10, MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(5), JAVA_INT.withName("y"))),
new PathElement[] { sequenceElement(), groupElement("y") },
new long[] { 4 },
(JAVA_INT.bitSize() + 5) * 4 + 5
});
testCases.add(new Object[] {
MemoryLayout.ofSequence(10, JAVA_INT),
new PathElement[] { sequenceElement() },
new long[] { 4 },
JAVA_INT.bitSize() * 4
});
testCases.add(new Object[] {
MemoryLayout.ofStruct(
MemoryLayout.ofSequence(10, JAVA_INT).withName("data")
),
new PathElement[] { groupElement("data"), sequenceElement() },
new long[] { 4 },
JAVA_INT.bitSize() * 4
});
MemoryLayout complexLayout = MemoryLayout.ofStruct(
MemoryLayout.ofSequence(10,
MemoryLayout.ofSequence(10,
MemoryLayout.ofStruct(
JAVA_INT.withName("x"),
JAVA_INT.withName("y")
)
)
).withName("data")
);
testCases.add(new Object[] {
complexLayout,
new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("x") },
new long[] { 0, 1 },
(JAVA_INT.bitSize() * 2)
});
testCases.add(new Object[] {
complexLayout,
new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("x") },
new long[] { 1, 0 },
(JAVA_INT.bitSize() * 2) * 10
});
testCases.add(new Object[] {
complexLayout,
new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("y") },
new long[] { 0, 1 },
(JAVA_INT.bitSize() * 2) + JAVA_INT.bitSize()
});
testCases.add(new Object[] {
complexLayout,
new PathElement[] { groupElement("data"), sequenceElement(), sequenceElement(), groupElement("y") },
new long[] { 1, 0 },
(JAVA_INT.bitSize() * 2) * 10 + JAVA_INT.bitSize()
});
return testCases.toArray(Object[][]::new);
}
}