8257184: Upstream 8252504: Add a method to MemoryLayout which returns a offset-computing method handle
Reviewed-by: mcimadamore, chegar
This commit is contained in:
parent
5a03e47605
commit
04ce8e3840
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user