Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MemoryLayout::byteOffset #157

Closed
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -97,7 +97,7 @@
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
* <p>
* Layout paths are for example useful in order to obtain offsets of arbitrarily nested layouts inside another layout
* (see {@link MemoryLayout#offset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected
* (see {@link MemoryLayout#bitOffset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}), to select an arbitrarily nested layout inside
* another layout (see {@link MemoryLayout#select(PathElement...)}, or to transform a nested layout element inside
* another layout (see {@link MemoryLayout#map(UnaryOperator, PathElement...)}).
@@ -112,9 +112,9 @@
));
* }</pre></blockquote>
*
* We can obtain the offset of the member layout named <code>value</code> from <code>seq</code>, as follows:
* We can obtain the offset, in bits, of the member layout named <code>value</code> from <code>seq</code>, as follows:
* <blockquote><pre>{@code
long valueOffset = seq.addOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
long valueOffset = seq.bitOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
* }</pre></blockquote>
*
* Similarly, we can select the member layout named {@code value}, as follows:
@@ -323,10 +323,30 @@ default long byteAlignment() {
* (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 offset(PathElement... elements) {
default long bitOffset(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offset, EnumSet.of(PathKind.SEQUENCE_ELEMENT, 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
* 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) {
return Utils.bitsToBytesOrThrow(bitOffset(elements),
() -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8"));
}

/**
* Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
* where the path is considered rooted in this layout.
@@ -36,64 +36,126 @@
import org.testng.annotations.*;

import java.util.List;
import java.util.function.Function;

import static org.testng.Assert.*;

public class TestLayoutPaths {

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromSeq() {
public void testBadBitSelectFromSeq() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.offset(PathElement.groupElement("foo"));
seq.bitOffset(PathElement.groupElement("foo"));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromStruct() {
public void testBadByteSelectFromSeq() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.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());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadByteSelectFromStruct() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.sequenceElement());
g.byteOffset(PathElement.sequenceElement());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadBitSelectFromValue() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadSelectFromValue() {
public void testBadByteSelectFromValue() {
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(), PathElement.sequenceElement());
seq.byteOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnknownStructField() {
public void testUnknownBitStructField() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.groupElement("foo"));
g.bitOffset(PathElement.groupElement("foo"));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnknownByteStructField() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.byteOffset(PathElement.groupElement("foo"));
}

@Test(expectedExceptions = NullPointerException.class)
public void testNullGroupElementName() {
PathElement.groupElement(null);
}

@Test(expectedExceptions = NullPointerException.class)
public void testBitNullGroupElementName() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.bitOffset(PathElement.groupElement(null));
}

@Test(expectedExceptions = NullPointerException.class)
public void testByteNullGroupElementName() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
g.offset(PathElement.groupElement(null));
g.byteOffset(PathElement.groupElement(null));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitOutOfBoundsSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(6));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testOutOfBoundsSeqIndex() {
public void testByteOutOfBoundsSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(6));
seq.byteOffset(PathElement.sequenceElement(6));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqIndex() {
PathElement.sequenceElement(-2);
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitNegativeSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(-2));
seq.bitOffset(PathElement.sequenceElement(-2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteNegativeSeqIndex() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.byteOffset(PathElement.sequenceElement(-2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testOutOfBoundsSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(6, 2));
seq.bitOffset(PathElement.sequenceElement(6, 2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testNegativeSeqRange() {
PathElement.sequenceElement(-2, 2);
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testBitNegativeSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.bitOffset(PathElement.sequenceElement(-2, 2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testByteNegativeSeqRange() {
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
seq.offset(PathElement.sequenceElement(-2, 2));
seq.byteOffset(PathElement.sequenceElement(-2, 2));
}

@Test(expectedExceptions = IllegalArgumentException.class)
@@ -102,11 +164,18 @@ public void testIncompleteAccess() {
seq.varHandle(int.class, PathElement.sequenceElement());
}

@Test(expectedExceptions = UnsupportedOperationException.class)
public void testBadMultiple() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(3), MemoryLayouts.JAVA_INT.withName("foo"));
g.byteOffset(PathElement.groupElement("foo"));
}

@Test
public void testBadContainerAlign() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
try {
g.offset(PathElement.groupElement("foo"));
g.bitOffset(PathElement.groupElement("foo"));
g.byteOffset(PathElement.groupElement("foo"));
} catch (Throwable ex) {
throw new AssertionError(ex); // should be ok!
}
@@ -124,7 +193,8 @@ public void testBadContainerAlign() {
public void testBadAlignOffset() {
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo"));
try {
g.offset(PathElement.groupElement("foo"));
g.bitOffset(PathElement.groupElement("foo"));
g.byteOffset(PathElement.groupElement("foo"));
} catch (Throwable ex) {
throw new AssertionError(ex); // should be ok!
}
@@ -144,7 +214,13 @@ public void testBadSequencePathInOffset() {
// bad path elements
for (PathElement e : List.of( PathElement.sequenceElement(), PathElement.sequenceElement(0, 2) )) {
try {
seq.offset(e);
seq.bitOffset(e);
fail();
} catch (IllegalArgumentException ex) {
assertTrue(true);
}
try {
seq.byteOffset(e);
fail();
} catch (IllegalArgumentException ex) {
assertTrue(true);
@@ -198,8 +274,10 @@ public void testStructPaths() {
// test offset

for (int i = 1 ; i <= 4 ; i++) {
long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], offset);
long bitOffset = g.bitOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], bitOffset);
long byteOffset = g.byteOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals((offsets[i - 1]) >>> 3, byteOffset);
}

// test map
@@ -237,8 +315,10 @@ public void testUnionPaths() {
// test offset

for (int i = 1 ; i <= 4 ; i++) {
long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], offset);
long bitOffset = g.bitOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals(offsets[i - 1], bitOffset);
long byteOffset = g.byteOffset(PathElement.groupElement(String.valueOf(i)));
assertEquals((offsets[i - 1]) >>> 3, byteOffset);
}

// test map
@@ -269,8 +349,10 @@ public void testSequencePaths() {
// test offset

for (int i = 0 ; i < 4 ; i++) {
long offset = g.offset(PathElement.sequenceElement(i));
assertEquals(offsets[i], offset);
long bitOffset = g.bitOffset(PathElement.sequenceElement(i));
assertEquals(offsets[i], bitOffset);
long byteOffset = g.byteOffset(PathElement.sequenceElement(i));
assertEquals((offsets[i]) >>> 3, byteOffset);
}

// test map
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.