There are two leaves in the layout hierarchy, value layouts,
which are used to represent values of given size and kind 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. Some common value layout constants, such
as ValueLayout#JAVA_INT and ValueLayout#JAVA_FLOAT_UNALIGNED are
defined in the ValueLayout class. A special kind of value layout, namely an
address layout, is used to model values that denote the
address of a region of memory.
More complex layouts can be derived from simpler ones: a sequence layout denotes a homogeneous repetition of zero or more occurrences of an element layout; a group layout denotes a heterogeneous aggregation of zero or more member layouts. Group layouts come in two flavors: struct layouts, where member layouts are laid out one after the other, and union layouts where member layouts are laid out at the same starting offset.
Layouts can be optionally associated with a name. A layout name can be referred to when constructing layout paths.
Consider the following struct declaration in C:
typedef struct { char kind; int value; } TaggedValues[5];
SequenceLayout TAGGED_VALUES = MemoryLayout.sequenceLayout(5, MemoryLayout.structLayout( ValueLayout.JAVA_BYTE.withName("kind"), MemoryLayout.paddingLayout(3), ValueLayout.JAVA_INT.withName("value") ) ).withName("TaggedValues");
ValueLayout#JAVA_INT
has carrier int, and size of 4 bytes;ValueLayout#ADDRESS has a size of 8 bytes on a 64-bit platform;Furthermore, all layouts have a natural alignment (expressed in bytes) which is defined as follows:
MemoryLayout#withByteAlignment(long)), which can be useful to describe
layouts with weaker or stronger alignment constraints.
Layout paths can be used to:
For instance, given the taggedValues sequence layout constructed above, we can
obtain the offset, in bytes, of the member layout named value in the
first sequence element, as follows:
long valueOffset = TAGGED_VALUES.byteOffset(PathElement.sequenceElement(0), PathElement.groupElement("value")); // yields 4
value, as follows:
MemoryLayout value = TAGGED_VALUES.select(PathElement.sequenceElement(), PathElement.groupElement("value"));
PathElement#sequenceElement(), PathElement#sequenceElement(long, long)
select an unspecified element in a sequence layout. A var handle derived from a
layout path containing one or more open path element features additional coordinates
of type long, which can be used by clients to bind the open elements
in the path:
VarHandle valueHandle = TAGGED_VALUES.varHandle(PathElement.sequenceElement(), PathElement.groupElement("value")); MemorySegment taggedValues = ... // reads the "value" field of the third struct in the array (taggedValues[2].value) int val = (int) valueHandle.get(taggedValues, 0L, // base offset 2L); // sequence index
Open path elements also affect the creation of
offset-computing method handles. Each
open path element becomes an additional long parameter in the obtained method
handle. This parameter can be used to specify the index of the sequence element whose
offset is to be computed:
MethodHandle offsetHandle = TAGGED_VALUES.byteOffsetHandle(PathElement.sequenceElement(), PathElement.groupElement("kind")); long offset1 = (long) offsetHandle.invokeExact(0L, 1L); // 0 + (1 * 8) = 8 long offset2 = (long) offsetHandle.invokeExact(0L, 2L); // 0 + (2 * 8) = 16
StructLayout RECTANGLE = MemoryLayout.structLayout( ValueLayout.ADDRESS.withTargetLayout( MemoryLayout.sequenceLayout(4, MemoryLayout.structLayout( ValueLayout.JAVA_INT.withName("x"), ValueLayout.JAVA_INT.withName("y") ).withName("point") ) ).withName("points") );
points, an address layout whose
target layout is a sequence layout of four
struct layouts. Each struct layout describes a two-dimensional point, and is defined
as a pair or ValueLayout#JAVA_INT coordinates, with names x and
y, respectively.
With dereference path elements, we can obtain a var handle that accesses the y coordinate of one of the
point in the rectangle, as follows:
VarHandle rectPointYs = RECTANGLE.varHandle( PathElement.groupElement("points"), PathElement.dereferenceElement(), PathElement.sequenceElement(), PathElement.groupElement("y") ); MemorySegment rect = ... // dereferences the third point struct in the "points" array, and reads its "y" coordinate (rect.points[2]->y) int rect_y_2 = (int) rectPointYs.get(rect, 0L, // base offset 2L); // sequence index
C_0, also called the
initial layout. Each path element in a layout path can be thought of as a
function that updates the current layout C_i-1 to some other layout
C_i. That is, for each path element E1, E2, ... En, in a layout path
P, we compute C_i = f_i(C_i-1), where f_i is the selection
function associated with the path element under consideration, denoted as E_i.
The final layout C_i is also called the selected layout.
A layout path P is considered well-formed for an initial layout C_0
if all its path elements E1, E2, ... En are well-formed for their
corresponding input layouts C_0, C_1, ... C_n-1. A path element E is
considered well-formed for a layout L if any of the following is true:
L is a sequence layout and E is a sequence path element
(one of PathElement#sequenceElement(long), PathElement#sequenceElement(long, long)
or PathElement#sequenceElement()). Moreover, if E contains one or
more sequence indices, such indices have to be compatible with the sequence layout's
element count;L is a group layout and E is a group path element (one of
PathElement#groupElement(String) or PathElement#groupElement(long)).
Moreover, the group path element must refer to a valid member layout in
L, either by name, or index;L is an address layout and E is a dereference path element.
Moreover, L must define some target layout.P that is not well-formed for an initial
layout C_0 will result in an IllegalArgumentException.
varHandle(PathElement...) or
ValueLayout#varHandle() features certain access characteristics, which are
derived from the selected layout L:
T, derived from L.carrier()A, derived from L.byteAlignment()S, derived from L.byteSize()A is compatible with the access size S, that is
if A >= S. An aligned var handle is guaranteed to support the following
access modes:
T. Access modes get and
set for long, double and MemorySegment
are supported but have no atomicity guarantee, as described in Section
17.7 of The Java Language Specification.
int, long,
float, double and MemorySegment.
(Future major platform releases of the JDK may support additional
types for certain currently unsupported access modes.)
int, long and MemorySegment.
(Future major platform releases of the JDK may support additional
numeric types for certain currently unsupported access modes.)
int, long and MemorySegment.
(Future major platform releases of the JDK may support additional
numeric types for certain currently unsupported access modes.)
T is float, double or MemorySegment then atomic
update access modes compare values using their bitwise representation
(see Float#floatToRawIntBits, Double#doubleToRawLongBits
and MemorySegment#address(), respectively).
Alternatively, a var handle is unaligned if its alignment constraint A
is incompatible with the access size S, that is, if A < S. An
unaligned var handle only supports the get and set access modes. All
other access modes will result in UnsupportedOperationException being thrown.
Moreover, while supported, access modes get and set might lead to
word tearing.
typedef struct { int x; int y; } Point;
x and
y respectively). Now consider the following snippet of C code:
int size = ... Point *points = (Point*)malloc(sizeof(Point) * size); for (int i = 0 ; i < size ; i++) { ... points[i].x ... }
points). Crucially, the size of
the array is dynamically bound to the value of the size variable. Inside
the loop, the x coordinate of all the points in the array is accessed.
To model this code in Java, let's start by defining a layout for the Point
struct, as follows:
StructLayout POINT = MemoryLayout.structLayout( ValueLayout.JAVA_INT.withName("x"), ValueLayout.JAVA_INT.withName("y") );
VarHandle POINT_ARR_X = POINT.arrayElementVarHandle(PathElement.groupElement("x")); int size = ... MemorySegment points = ... for (int i = 0 ; i < size ; i++) { ... POINT_ARR_X.get(points, 0L, (long)i) ... }
x of subsequent point in the array is accessed using the
POINT_ARR_X var handle, which is obtained using the
arrayElementVarHandle(PathElement...) method. This var handle features two
long coordinates: the first is a base offset (set to 0L), while the
second is a logical index that can be used to stride over all the elements of the
point array.
The base offset coordinate allows clients to express complex access operations, by
injecting additional offset computation into the var handle (we will see an example
of that below). In cases where the base offset is constant (as in the previous
example) clients can, if desired, drop the base offset parameter and make the access
expression simpler. This is achieved using the
java.
var handle adapter.
typedef struct { int size; Point points[]; } Polygon;
points
array is left unspecified in the C declaration, using a
Flexible Array Member (a feature standardized in C99).
Again, clients can perform structured access to elements in the nested variable-length
array using the arrayElementVarHandle(PathElement. method, as demonstrated
below:
StructLayout POLYGON = MemoryLayout.structLayout( ValueLayout.JAVA_INT.withName("size"), MemoryLayout.sequenceLayout(0, POINT).withName("points") ); VarHandle POLYGON_SIZE = POLYGON.varHandle(0, PathElement.groupElement("size")); long POINTS_OFFSET = POLYGON.byteOffset(PathElement.groupElement("points"));
POLYGON layout contains a sequence layout of size zero. The
element layout of the sequence layout is the POINT layout, shown previously.
The polygon layout is used to obtain a var handle that provides access to the polygon
size, as well as an offset (POINTS_OFFSET) to the start of the variable-length
points array.
The x coordinates of all the points in a polygon can then be accessed as
follows:
MemorySegment polygon = ... int size = POLYGON_SIZE.get(polygon, 0L); for (int i = 0 ; i < size ; i++) { ... POINT_ARR_X.get(polygon, POINTS_OFFSET, (long)i) ... }
POLYGON_SIZE var handle.
Then, in a loop, we read the x coordinates of all the points in the polygon.
This is done by providing a custom offset (namely, POINTS_OFFSET) to the
offset coordinate of the POINT_ARR_X var handle. As before, the loop
induction variable i is passed as the index of the POINT_ARR_X
var handle, to stride over all the elements of the variable-length array.
Implementation Specification
Implementations of this interface are immutable, thread-safe and value-based.
| Modifier and Type | Class and Description |
|---|---|
| public static interface | MemoryLayout.
An element in a layout path. |
| Modifier and Type | Method and Description |
|---|---|
| public VarHandle | Returns: a var handle that accesses adjacent elements in a memory segment at offsets selected by the given layout paththe layout path elements elementsCreates a var handle that accesses adjacent elements in a memory segment at offsets selected by the given layout path, where the accessed elements have this layout, and where the initial layout in the path is this layout. |
| public long | Returns: the alignment constraint associated with this layout, expressed in bytesReturns the alignment constraint associated with this layout, expressed in bytes. |
| public long | Returns: The offset, in bytes, of the layout selected by the layout path inelementsthe layout path elements elementsComputes the offset, in bytes, of the layout selected by the given layout path, where the initial layout in the path is this layout. |
| public MethodHandle | Returns: a method handle that computes the offset, in bytes, of the layout selected by the given layout paththe layout path elements elementsCreates a method handle that computes the offset, in bytes, of the layout selected by the given layout path, where the initial layout in the path is this layout. |
| public long | |
| public boolean | |
| public int | |
| public Optional | Returns: the name (if any) associated with this layoutReturns the name (if any) associated with this layout. |
| public static PaddingLayout | Returns: the new selector layoutthe padding size (expressed in bytes) byteSize)Creates a padding layout with the given byte size. |
| public long | Returns: offset + (byteSize() * index)the base offset offset, long the index to be scaled by the byte size of this layout index)Returns |
| public MethodHandle | Returns: a method handle that can be used to invokescale(long, long)
on this layoutReturns a method handle that can be used to invoke |
| public MemoryLayout | Returns: the layout selected by the layout path inelementsthe layout path elements elementsReturns the layout selected from the provided path, where the initial layout in the path is this layout. |
| public static SequenceLayout | Returns: the new sequence layout with the given element layout and sizethe sequence element count elementCount, MemoryLayout the sequence element layout elementLayout)Creates a sequence layout with the given element layout and element count. |
| public MethodHandle | Returns: a method handle that is used to slice a memory segment at the offset selected by the given layout paththe layout path elements elementsCreates a method handle which, given a memory segment, returns a slice corresponding to the layout selected by the given layout path, where the initial layout in the path is this layout. |
| public static StructLayout | Returns: a struct layout with the given member layoutsThe member layouts of the struct layout elements)Creates a struct layout with the given member layouts. |
| public String | Returns: the string representation of this layoutReturns the string representation of this layout. |
| public static UnionLayout | Returns: a union layout with the given member layoutsThe member layouts of the union layout elements)Creates a union layout with the given member layouts. |
| public VarHandle | Returns: a var handle that accesses a memory segment at the offset selected by the given layout paththe layout path elements elementsCreates a var handle that accesses a memory segment at the offset selected by the given layout path, where the initial layout in the path is this layout. |
| public MemoryLayout | Returns: a memory layout with the same characteristics as this layout, but with the given alignment constraint (in bytes)the layout alignment constraint, expressed in bytes byteAlignment)Returns a memory layout with the same characteristics as this layout, but with the given alignment constraint (in bytes). |
| public MemoryLayout | |
| public MemoryLayout | Returns: a memory layout with the same characteristics as this layout, but with no nameReturns a memory layout with the same characteristics as this layout, but with no name. |
| arrayElementVarHandle | back to summary |
|---|---|
| public VarHandle arrayElementVarHandle(MemoryLayout. Creates a var handle that accesses adjacent elements in a memory segment at offsets selected by the given layout path, where the accessed elements have this layout, and where the initial layout in the path is this layout. The returned var handle has the following characteristics:
If the provided layout path O = this.byteOffsetHandle(P).invokeExact(this.scale(B, I0), I1, I2, ... In);
More formally, the method handle returned by this method is obtained from MethodHandles.collectCoordinates(varHandle(elements), 1, scaleHandle()) Accessing a memory segment using the var handle returned by this method is subject to the following checks:
API Note As the leading index coordinate
| |
| byteAlignment | back to summary |
|---|---|
| public long byteAlignment() Returns the alignment constraint associated with this layout, expressed in bytes.
Layout alignment defines a power of two
withByteAlignment(long)), then this method returns the
natural alignment constraint (in bytes) associated
with this layout.
| |
| byteOffset | back to summary |
|---|---|
| public long byteOffset(MemoryLayout. Computes the offset, in bytes, of the layout selected by the given layout path, where the initial layout in the path is this layout.
| |
| byteOffsetHandle | back to summary |
|---|---|
| public MethodHandle byteOffsetHandle(MemoryLayout. Creates a method handle that computes the offset, in bytes, of the layout selected by the given layout path, where the initial layout in the path is this layout. The returned method handle has the following characteristics:
The final offset returned by the method handle is computed as follows: where b represents the base offset provided as a dynamic
long argument, x_1, x_2, ... x_n represent indices
into sequences provided as dynamic long arguments, whereas
s_1, s_2, ... s_n are static stride constants
derived from the size of the element layout of a sequence, and
c_1, c_2, ... c_m are other static offset
constants (such as field offsets) which are derived from the layout path.
For any given dynamic argument API Note The returned method handle can be used to compute a layout offset,
similarly to
| |
| byteSize | back to summary |
|---|---|
| public long byteSize() Returns the layout size, in bytes.
| |
| equals | back to summary |
|---|---|
| public boolean equals(Object other) Compares the specified object with this layout for equality. Returns
| |
| hashCode | back to summary |
|---|---|
| public int hashCode() Returns the hash code value for this layout.
| |
| name | back to summary |
|---|---|
| public Optional Returns the name (if any) associated with this layout.
| |
| paddingLayout | back to summary |
|---|---|
| public static PaddingLayout paddingLayout(long byteSize) Creates a padding layout with the given byte size. The alignment constraint of the returned layout is 1. As such, regardless of its size, in the absence of an explicit alignment constraint, a padding layout does not affect the natural alignment of the group or sequence layout it is nested into.
| |
| scale | back to summary |
|---|---|
| public long scale(long offset, long index) Returns
| |
| scaleHandle | back to summary |
|---|---|
| public MethodHandle scaleHandle() Returns a method handle that can be used to invoke
| |
| select | back to summary |
|---|---|
| public MemoryLayout select(MemoryLayout. Returns the layout selected from the provided path, where the initial layout in the path is this layout.
| |
| sequenceLayout | back to summary |
|---|---|
| public static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) Creates a sequence layout with the given element layout and element count.
| |
| sliceHandle | back to summary |
|---|---|
| public MethodHandle sliceHandle(MemoryLayout. Creates a method handle which, given a memory segment, returns a slice corresponding to the layout selected by the given layout path, where the initial layout in the path is this layout. The returned method handle has the following characteristics:
The offset Computing a slice of a memory segment using the method handle returned by this method is subject to the following checks:
API Note The returned method handle can be used to obtain a memory segment slice,
similarly to
| |
| structLayout | back to summary |
|---|---|
| public static StructLayout structLayout(MemoryLayout... elements) Creates a struct layout with the given member layouts. API Note This factory does not automatically align element layouts, by inserting additional padding layout elements. As such, the following struct layout creation will fail with an exception: structLayout(JAVA_SHORT, JAVA_INT); structLayout(JAVA_SHORT, MemoryLayout.paddingLayout(2), JAVA_INT); structLayout(JAVA_SHORT, JAVA_INT.withByteAlignment(2));
| |
| toString | back to summary |
|---|---|
| public String toString() Returns the string representation of this layout. | |
| unionLayout | back to summary |
|---|---|
| public static UnionLayout unionLayout(MemoryLayout... elements) Creates a union layout with the given member layouts.
| |
| varHandle | back to summary |
|---|---|
| public VarHandle varHandle(MemoryLayout. Creates a var handle that accesses a memory segment at the offset selected by the given layout path, where the initial layout in the path is this layout. The returned var handle has the following characteristics:
If the provided layout path O = this.byteOffsetHandle(P).invokeExact(B, I1, I2, ... In); Accessing a memory segment using the var handle returned by this method is subject to the following checks:
If the selected layout is an address layout, calling
VarHandle#set(Object. can throw IllegalArgumentException
if the memory segment representing the address to be written is not a
native memory segment.
If the provided layout path has size VarHandle baseHandle = this.varHandle(P); MemoryLayout target = ((AddressLayout)this.select(P)).targetLayout().get(); VarHandle targetHandle = target.varHandle(Q); targetHandle = MethodHandles.insertCoordinates(targetHandle, 1, 0L); // always access nested targets at offset 0 targetHandle = MethodHandles.collectCoordinates(targetHandle, 0, baseHandle.toMethodHandle(VarHandle.AccessMode.GET));
As an example, consider the memory layout expressed by a GroupLayout grp = java.lang.foreign.MemoryLayout.structLayout( MemoryLayout.paddingLayout(4), ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value") ); value, we can construct a var handle as
follows:
VarHandle handle = grp.varHandle(PathElement.groupElement("value")); //(MemorySegment, long) -> int API Note The resulting var handle features certain access mode restrictions, which are common to all var handles derived from memory layouts.
| |
| withByteAlignment | back to summary |
|---|---|
| public MemoryLayout withByteAlignment(long byteAlignment) Returns a memory layout with the same characteristics as this layout, but with the given alignment constraint (in bytes).
| |
| withName | back to summary |
|---|---|
| public MemoryLayout withName(String name) Returns a memory layout with the same characteristics as this layout, but with the given name.
| |
| withoutName | back to summary |
|---|---|
| public MemoryLayout withoutName() Returns a memory layout with the same characteristics as this layout, but with no name. API Note This can be useful to compare two layouts that have different names, but are otherwise equal.
| |
GroupLayout, either by name or by index;SequenceLayout; andImplementation Specification
Implementations of this interface are immutable, thread-safe and value-based.
| Modifier and Type | Method and Description |
|---|---|
| public static MemoryLayout. | Returns: a path element that selects the target layout of an address layout (where set)Returns a path element that selects the target layout of an address layout (where set). |
| public static MemoryLayout. | Returns: a path element which selects a member layout with the given name in a group layoutthe name of the member layout to be selected name)Returns a path element which selects a member layout with the given name in a group layout. |
| public static MemoryLayout. | Returns: a path element that selects a member layout with the given index in a group layoutthe index of the member layout element to be selected index)Returns a path element that selects a member layout with the given index in a group layout. |
| public static MemoryLayout. | Returns: a path element which selects the element layout at the specified index in a sequence layoutthe index of the sequence element to be selected index)Returns a path element which selects the element layout at the specified index in a sequence layout. |
| public static MemoryLayout. | Returns: a path element that selects the sequence element layout with the given index.the index of the first sequence element to be selected start, long the step factor at which subsequence sequence elements are to be
selected step)Returns an open path element that selects the element layout in a range of positions in a sequence layout. |
| public static MemoryLayout. | Returns: an open path element that selects an unspecified element layout in a sequence layoutReturns an open path element that selects an unspecified element layout in a sequence layout. |
| dereferenceElement | back to summary |
|---|---|
| public static MemoryLayout. Returns a path element that selects the target layout of an address layout (where set).
| |
| groupElement | back to summary |
|---|---|
| public static MemoryLayout. Returns a path element which selects a member layout with the given name in a group layout. Implementation Specification in case multiple group elements with a matching name exist, the path
element returned by this method will select the first one; that is,
the group element with the lowest offset from the current path is
selected. In such cases, using
| |
| groupElement | back to summary |
|---|---|
| public static MemoryLayout. Returns a path element that selects a member layout with the given index in a group layout.
| |
| sequenceElement | back to summary |
|---|---|
| public static MemoryLayout. Returns a path element which selects the element layout at the specified index in a sequence layout.
| |
| sequenceElement | back to summary |
|---|---|
| public static MemoryLayout. Returns an open path element
that selects the element layout in a range of positions in a sequence
layout. The range is expressed as a pair of starting index (inclusive)
The exact sequence element selected by this layout is expressed as an index
B.
| |
| sequenceElement | back to summary |
|---|---|
| public static MemoryLayout. Returns an open path element that selects an unspecified element layout in a sequence layout.
The exact sequence element selected by this layout is expressed as an index
| |