Interface MemoryLayout
- All Superinterfaces:
Constable
- All Known Implementing Classes:
-
GroupLayout
,SequenceLayout
,ValueLayout
public sealed interface MemoryLayout extends Constable permits SequenceLayout, GroupLayout, ValueLayout (not exhaustive)
ValueLayout
) and padding layouts which are used, as the name suggests, to represent a portion of a memory segment whose contents should be ignored, and which are primarily present for alignment reasons (see paddingLayout(long)
). Some common value layout constants are defined in the MemoryLayouts
class. More complex layouts can be derived from simpler ones: a sequence layout denotes a repetition of one or more element layout (see SequenceLayout
); a group layout denotes an aggregation of (typically) heterogeneous member layouts (see GroupLayout
).
For instance, consider the following struct declaration in C:
The above declaration can be modelled using a layout object, as follows:typedef struct { char kind; int value; } TaggedValues[5];
SequenceLayout taggedValues = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout( MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"), MemoryLayout.paddingLayout(24), MemoryLayout.valueLayout(32, ByteOrder.nativeOrder()).withName("value") ) ).withName("TaggedValues");
All implementations of this interface must be value-based; programmers should treat instances that are equal as interchangeable and should not use instances for synchronization, or unpredictable behavior may occur. For example, in a future release, synchronization may fail. The equals
method should be used for comparisons.
Non-platform classes should not implement MemoryLayout directly.
Unless otherwise specified, passing a null
argument, or an array argument containing one or more null
elements to a method in this class causes a NullPointerException
to be thrown.
Size, alignment and byte order
All layouts have a size; layout size for value and padding layouts is always explicitly denoted; this means that a layout description always has the same size in bits, regardless of the platform in which it is used. For derived layouts, the size is computed as follows:- for a finite sequence layout S whose element layout is E and size is L, the size of S is that of E, multiplied by L
- the size of an unbounded sequence layout is unknown
- for a group layout G with member layouts M1, M2, ... Mn whose sizes are S1, S2, ... Sn, respectively, the size of G is either S1 + S2 + ... + Sn or max(S1, S2, ... Sn) depending on whether the group is a struct or an union, respectively
Furthermore, all layouts feature a natural alignment which can be inferred as follows:
- for a padding layout L, the natural alignment is 1, regardless of its size; that is, in the absence of an explicit alignment constraint, a padding layout should not affect the alignment constraint of the group layout it is nested into
- for a value layout L whose size is N, the natural alignment of L is N
- for a sequence layout S whose element layout is E, the natural alignment of S is that of E
- for a group layout G with member layouts M1, M2, ... Mn whose alignments are A1, A2, ... An, respectively, the natural alignment of G is max(A1, A2 ... An)
withBitAlignment(long)
), which can be useful to describe hyper-aligned layouts. All value layouts have an explicit byte order (see ByteOrder
) which is set when the layout is created.
Layout paths
A layout path originates from a root layout (typically a group or a sequence layout) and terminates at a layout nested within the root layout - this is the layout selected by the layout path. Layout paths are typically expressed as a sequence of one or moreMemoryLayout.PathElement
instances. Layout paths are for example useful in order to obtain offsets of arbitrarily nested layouts inside another layout (see bitOffset(PathElement...)
), to quickly obtain a memory access handle corresponding to the selected layout (see varHandle(Class, PathElement...)
), to select an arbitrarily nested layout inside another layout (see select(PathElement...)
, or to transform a nested layout element inside another layout (see map(UnaryOperator, PathElement...)
).
Such layout paths can be constructed programmatically using the methods in this class. For instance, given the taggedValues
layout instance constructed as above, we can obtain the offset, in bits, of the member layout named value
in the first sequence element, as follows:
Similarly, we can select the member layout namedlong valueOffset = taggedValues.bitOffset(PathElement.sequenceElement(0), PathElement.groupElement("value")); // yields 32
value
, as follows: And, we can also replace the layout namedMemoryLayout value = taggedValues.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
value
with another layout, as follows: That is, the above declaration is identical to the following, more verbose one:MemoryLayout taggedValuesWithHole = taggedValues.map(l -> MemoryLayout.paddingLayout(32), PathElement.sequenceElement(), PathElement.groupElement("value"));
Layout paths can feature one or more free dimensions. For instance, a layout path traversing an unspecified sequence element (that is, where one of the path component was obtained with theMemoryLayout taggedValuesWithHole = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout( MemoryLayout.valueLayout(8, ByteOrder.nativeOrder()).withName("kind"), MemoryLayout.paddingLayout(32), MemoryLayout.paddingLayout(32) ));
MemoryLayout.PathElement.sequenceElement()
method) features an additional free dimension, which will have to be bound at runtime. This is important when obtaining memory access var handle from layouts, as in the following code: Since the layout path constructed in the above example features exactly one free dimension (as it doesn't specify which member layout namedVarHandle valueHandle = taggedValues.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
value
should be selected from the enclosing sequence layout), it follows that the memory access var handle valueHandle
will feature an additional long
access coordinate. A layout path with free dimensions can also be used to create an offset-computing method handle, using the bitOffset(PathElement...)
or byteOffsetHandle(PathElement...)
method. Again, free dimensions are translated into 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:
MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(), PathElement.groupElement("kind")); long offset1 = (long) offsetHandle.invokeExact(1L); // 8 long offset2 = (long) offsetHandle.invokeExact(2L); // 16
Layout attributes
Layouts can be optionally associated with one or more attributes. A layout attribute forms a name/value pair, where the name is aString
and the value is a Constable
. The most common form of layout attribute is the layout name (see LAYOUT_NAME
), a custom name that can be associated with memory layouts and that can be referred to when constructing layout paths.- Implementation Requirements:
- Implementations of this interface are immutable, thread-safe and value-based.
Nested Class Summary
Modifier and Type | Interface | Description |
---|---|---|
static interface |
MemoryLayout.PathElement |
Instances of this class are used to form layout paths. |
Field Summary
Modifier and Type | Field | Description |
---|---|---|
static final String |
LAYOUT_NAME |
Attribute name used to specify the name property of a memory layout (see name() and withName(String) ). |
Method Summary
Modifier and Type | Method | Description |
---|---|---|
Optional<Constable> |
attribute |
Returns the attribute with the given name (if it exists). |
Stream<String> |
attributes() |
Returns a stream of the attribute names associated with this layout. |
long |
bitAlignment() |
Returns the alignment constraint associated with this layout, expressed in bits. |
default long |
bitOffset |
Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this layout. |
default MethodHandle |
bitOffsetHandle |
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. |
long |
bitSize() |
Computes the layout size, in bits. |
default long |
byteAlignment() |
Returns the alignment constraint associated with this layout, expressed in bytes. |
default long |
byteOffset |
Computes the offset, in bytes, of the layout selected by a given layout path, where the path is considered rooted in this layout. |
default MethodHandle |
byteOffsetHandle |
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. |
default long |
byteSize() |
Computes the layout size, in bytes. |
Optional<? extends DynamicConstantDesc<? extends MemoryLayout>> |
describeConstable() |
|
boolean |
equals |
Compares the specified object with this layout for equality. |
int |
hashCode() |
Returns the hash code value for this layout. |
boolean |
hasSize() |
Does this layout have a specified size? |
boolean |
isPadding() |
Is this a padding layout (e.g. a layout created from paddingLayout(long) ) ? |
default MemoryLayout |
map |
Creates a transformed copy of this layout where a selected layout, from a path rooted in this layout, is replaced with the result of applying the given operation. |
Optional<String> |
name() |
Return the name (if any) associated with this layout. |
static MemoryLayout |
paddingLayout |
Create a new padding layout with given size. |
default MemoryLayout |
select |
Selects the layout from a path rooted in this layout. |
static SequenceLayout |
sequenceLayout |
Create a new sequence layout with given element layout and element count. |
static SequenceLayout |
sequenceLayout |
Create a new sequence layout, with unbounded element count and given element layout. |
default MethodHandle |
sliceHandle |
Creates a method handle which, given a memory segment, returns a slice corresponding to the layout selected by a given layout path, where the path is considered rooted in this layout. |
static GroupLayout |
structLayout |
Create a new struct group layout with given member layouts. |
String |
toString() |
Returns a string representation of this layout. |
static GroupLayout |
unionLayout |
Create a new union group layout with given member layouts. |
static ValueLayout |
valueLayout |
Create a value layout of given byte order and size. |
default VarHandle |
varHandle |
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. |
MemoryLayout |
withAttribute |
Returns a new memory layout which features the same attributes as this layout, plus the newly specified attribute. |
MemoryLayout |
withBitAlignment |
Creates a new layout which features the desired alignment constraint. |
MemoryLayout |
withName |
Creates a new layout which features the desired layout name. |
Field Details
LAYOUT_NAME
static final String LAYOUT_NAME
name()
and withName(String)
).- See Also:
Method Details
describeConstable
Optional<? extends DynamicConstantDesc<? extends MemoryLayout>> describeConstable()
Optional
containing the nominal descriptor for this layout, if one can be constructed, or an empty Optional
if one cannot be constructed.- Specified by:
-
describeConstable
in interfaceConstable
- Returns:
- An
Optional
containing the resulting nominal descriptor, or an emptyOptional
if one cannot be constructed.
hasSize
boolean hasSize()
SequenceLayout.elementCount()
). Value layouts (see ValueLayout
) and padding layouts (see paddingLayout(long)
) always have a specified size, therefore this method always returns true
in these cases.- Returns:
-
true
, if this layout has a specified size.
bitSize
long bitSize()
- Returns:
- the layout size, in bits.
- Throws:
-
UnsupportedOperationException
- if the layout is, or contains, a sequence layout with unspecified size (seeSequenceLayout
).
byteSize
default long byteSize()
- Returns:
- the layout size, in bytes.
- Throws:
-
UnsupportedOperationException
- if the layout is, or contains, a sequence layout with unspecified size (seeSequenceLayout
), or ifbitSize()
is not a multiple of 8.
name
Optional<String> name()
This is equivalent to the following code:
attribute(LAYOUT_NAME).map(String.class::cast);
- Returns:
- the layout name (if any).
- See Also:
withName
MemoryLayout withName(String name)
This is equivalent to the following code:
withAttribute(LAYOUT_NAME, name);
- Parameters:
-
name
- the layout name. - Returns:
- a new layout which is the same as this layout, except for the name associated with it.
- See Also:
bitAlignment
long bitAlignment()
A
which is the bit-wise alignment of the layout. If A <= 8
then A/8
is the number of bytes that must be aligned for any pointer that correctly points to this layout. Thus: -
A=8
means unaligned (in the usual sense), which is common in packets. -
A=64
means word aligned (on LP64),A=32
int aligned,A=16
short aligned, etc. -
A=512
is the most strict alignment required by the x86/SV ABI (for AVX-512 data).
withBitAlignment(long)
), then this method returns the natural alignment constraint (in bits) associated with this layout.- Returns:
- the layout alignment constraint, in bits.
byteAlignment
default long byteAlignment()
A
which is the byte-wise alignment of the layout, where A
is the number of bytes that must be aligned for any pointer that correctly points to this layout. Thus: -
A=1
means unaligned (in the usual sense), which is common in packets. -
A=8
means word aligned (on LP64),A=4
int aligned,A=2
short aligned, etc. -
A=64
is the most strict alignment required by the x86/SV ABI (for AVX-512 data).
withBitAlignment(long)
), then this method returns the natural alignment constraint (in bytes) associated with this layout.- Returns:
- the layout alignment constraint, in bytes.
- Throws:
-
UnsupportedOperationException
- ifbitAlignment()
is not a multiple of 8.
withBitAlignment
MemoryLayout withBitAlignment(long bitAlignment)
- Parameters:
-
bitAlignment
- the layout alignment constraint, expressed in bits. - Returns:
- a new layout which is the same as this layout, except for the alignment constraint associated with it.
- Throws:
-
IllegalArgumentException
- ifbitAlignment
is not a power of two, or if it's less than than 8.
attribute
Optional<Constable> attribute(String name)
- Parameters:
-
name
- the attribute name - Returns:
- the attribute with the given name (if it exists).
withAttribute
MemoryLayout withAttribute(String name, Constable value)
- Parameters:
-
name
- the attribute name. -
value
- the attribute value. - Returns:
- a new memory layout which features the same attributes as this layout, plus the newly specified attribute.
attributes
Stream<String> attributes()
- Returns:
- a stream of the attribute names associated with this layout.
bitOffset
default long bitOffset(MemoryLayout.PathElement... elements)
- Parameters:
-
elements
- the layout path elements. - Returns:
- The offset, in bits, of the layout selected by the layout path in
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 (seeMemoryLayout.PathElement.sequenceElement()
andMemoryLayout.PathElement.sequenceElement(long, long)
). -
UnsupportedOperationException
- if one of the layouts traversed by the layout path has unspecified size. -
NullPointerException
- if eitherelements == null
, or if any of the elements inelements
isnull
.
bitOffsetHandle
default MethodHandle bitOffsetHandle(MemoryLayout.PathElement... elements)
The returned method handle has a return type of long
, and features as many long
parameter types as there are free dimensions in the provided layout path (see MemoryLayout.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 bitOffset(PathElement...)
, but where some sequence indices are specified only when invoking the method handle.
The final offset returned by the method handle is computed as follows:
whereoffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
x_1
, x_2
, ... x_n
are dynamic values provided as long
arguments, whereas c_1
, c_2
, ... c_m
are static offset constants and s_0
, s_1
, ... s_n
are static stride constants which are derived from the layout path.- Parameters:
-
elements
- the layout path elements. - Returns:
- 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 (seeMemoryLayout.PathElement.sequenceElement(long, long)
). -
UnsupportedOperationException
- if one of the layouts traversed by the layout path has unspecified size.
byteOffset
default long byteOffset(MemoryLayout.PathElement... elements)
- Parameters:
-
elements
- the layout path elements. - Returns:
- The offset, in bytes, of the layout selected by the layout path in
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 (seeMemoryLayout.PathElement.sequenceElement()
andMemoryLayout.PathElement.sequenceElement(long, long)
). -
UnsupportedOperationException
- if one of the layouts traversed by the layout path has unspecified size, or ifbitOffset(elements)
is not a multiple of 8. -
NullPointerException
- if eitherelements == null
, or if any of the elements inelements
isnull
.
byteOffsetHandle
default MethodHandle byteOffsetHandle(MemoryLayout.PathElement... elements)
The returned method handle has a return type of long
, and features as many long
parameter types as there are free dimensions in the provided layout path (see MemoryLayout.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 byteOffset(PathElement...)
, but where some sequence indices are specified only when invoking the method handle.
The final offset returned by the method handle is computed as follows:
wherebitOffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n) offset = bitOffset / 8
x_1
, x_2
, ... x_n
are dynamic values provided as long
arguments, whereas c_1
, c_2
, ... c_m
are static offset constants and s_0
, s_1
, ... s_n
are static stride constants which are derived from the layout path. The method handle will throw an UnsupportedOperationException
if the computed offset in bits is not a multiple of 8.
- Parameters:
-
elements
- the layout path elements. - Returns:
- 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 (seeMemoryLayout.PathElement.sequenceElement(long, long)
). -
UnsupportedOperationException
- if one of the layouts traversed by the layout path has unspecified size.
varHandle
default VarHandle varHandle(Class<?> carrier, MemoryLayout.PathElement... elements)
The final memory location accessed by the returned memory access var handle can be computed as follows:
whereaddress = base + offset
base
denotes the base address expressed by the MemorySegment
access coordinate (see MemorySegment.address()
and MemoryAddress.toRawLongValue()
) and offset
can be expressed in the following form: whereoffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
x_1
, x_2
, ... x_n
are dynamic values provided as long
arguments, whereas c_1
, c_2
, ... c_m
are static offset constants and s_0
, s_1
, ... s_n
are static stride constants which are derived from the layout path.- API Note:
- the resulting var handle will feature an additional
long
access coordinate for every unspecified sequence access component contained in this layout path. Moreover, the resulting var handle features certain access mode restrictions, which are common to all memory access var handles. - Parameters:
-
carrier
- the var handle carrier type. -
elements
- the layout path elements. - Returns:
- a var handle which can be used to dereference memory at the (possibly nested) layout selected by the layout path in
elements
. - Throws:
-
UnsupportedOperationException
- if the layout path has one or more elements with incompatible alignment constraints, or if one of the layouts traversed by the layout path has unspecified size. -
IllegalArgumentException
- if the carrier does not represent a primitive type, if the carrier isvoid
,boolean
, or if the layout path inelements
does not select a value layout (seeValueLayout
), or if the selected value layout has a size that that does not match that of the specified carrier type.
sliceHandle
default MethodHandle sliceHandle(MemoryLayout.PathElement... elements)
The returned method handle has a return type of MemorySegment
, features a MemorySegment
parameter as leading parameter representing the segment to be sliced, and features as many trailing long
parameter types as there are free dimensions in the provided layout path (see MemoryLayout.PathElement.sequenceElement()
, where the order of the parameters corresponds to the order of the path elements. The returned method handle can be used to create a slice similar to using MemorySegment.asSlice(long, long)
, but where the offset argument is dynamically compute based on indices specified when invoking the method handle.
The offset of the returned segment is computed as follows:
wherebitOffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n) offset = bitOffset / 8
x_1
, x_2
, ... x_n
are dynamic values provided as long
arguments, whereas c_1
, c_2
, ... c_m
are static offset constants and s_0
, s_1
, ... s_n
are static stride constants which are derived from the layout path. After the offset is computed, the returned segment is create as if by calling:
wheresegment.asSlice(offset, layout.byteSize());
segment
is the segment to be sliced, and where layout
is the layout selected by the given layout path, as per select(PathElement...)
. The method handle will throw an UnsupportedOperationException
if the computed offset in bits is not a multiple of 8.
- Parameters:
-
elements
- the layout path elements. - Returns:
- a method handle which can be used to create a slice of the selected layout element, given a segment.
- Throws:
-
UnsupportedOperationException
- if the size of the selected layout in bits is not a multiple of 8.
select
default MemoryLayout select(MemoryLayout.PathElement... elements)
- Parameters:
-
elements
- the layout path elements. - Returns:
- the layout selected by the layout path in
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 one or more sequence element indices (seeMemoryLayout.PathElement.sequenceElement(long)
andMemoryLayout.PathElement.sequenceElement(long, long)
).
map
default MemoryLayout map(UnaryOperator<MemoryLayout> op, MemoryLayout.PathElement... elements)
- Parameters:
-
op
- the unary operation to be applied to the selected layout. -
elements
- the layout path elements. - Returns:
- a new layout where the layout selected by the layout path in
elements
, has been replaced by the result of applyingop
to the selected layout. - 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 one or more sequence element indices (seeMemoryLayout.PathElement.sequenceElement(long)
andMemoryLayout.PathElement.sequenceElement(long, long)
).
isPadding
boolean isPadding()
paddingLayout(long)
) ?- Returns:
- true, if this layout is a padding layout.
equals
boolean equals(Object that)
true
if and only if the specified object is also a layout, and it is equal to this layout. Two layouts are considered equal if they are of the same kind, have the same size, name and alignment constraints. Furthermore, depending on the layout kind, additional conditions must be satisfied: - two value layouts are considered equal if they have the same byte order (see
ValueLayout.order()
) - two sequence layouts are considered equal if they have the same element count (see
SequenceLayout.elementCount()
), and if their element layouts (seeSequenceLayout.elementLayout()
) are also equal - two group layouts are considered equal if they are of the same kind (see
GroupLayout.isStruct()
,GroupLayout.isUnion()
) and if their member layouts (seeGroupLayout.memberLayouts()
) are also equal
hashCode
int hashCode()
toString
String toString()
paddingLayout
static MemoryLayout paddingLayout(long size)
- Parameters:
-
size
- the padding size in bits. - Returns:
- the new selector layout.
- Throws:
-
IllegalArgumentException
- ifsize <= 0
.
valueLayout
static ValueLayout valueLayout(long size, ByteOrder order)
- Parameters:
-
size
- the value layout size. -
order
- the value layout's byte order. - Returns:
- a new value layout.
- Throws:
-
IllegalArgumentException
- ifsize <= 0
.
sequenceLayout
static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout)
- Parameters:
-
elementCount
- the sequence element count. -
elementLayout
- the sequence element layout. - Returns:
- the new sequence layout with given element layout and size.
- Throws:
-
IllegalArgumentException
- ifelementCount < 0
.
sequenceLayout
static SequenceLayout sequenceLayout(MemoryLayout elementLayout)
- Parameters:
-
elementLayout
- the element layout of the sequence layout. - Returns:
- the new sequence layout with given element layout.
structLayout
static GroupLayout structLayout(MemoryLayout... elements)
- Parameters:
-
elements
- The member layouts of the struct group layout. - Returns:
- a new struct group layout with given member layouts.
unionLayout
static GroupLayout unionLayout(MemoryLayout... elements)
- Parameters:
-
elements
- The member layouts of the union layout. - Returns:
- a new union group layout with given member layouts.
© 1993, 2021, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/17/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/MemoryLayout.html