Spec-Zone.ru › OpenJDK 24

Интерфейс MemoryLayout

Все известные подинтерфейсы:
AddressLayout, GroupLayout, PaddingLayout, SequenceLayout, StructLayout, UnionLayout, ValueLayout, ValueLayout.OfBoolean, ValueLayout.OfByte, ValueLayout.OfChar, ValueLayout.OfDouble, ValueLayout.OfFloat, ValueLayout.OfInt, ValueLayout.OfLong, ValueLayout.OfShort
public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, PaddingLayout, ValueLayout
Макет памяти описывает содержимое сегмента памяти.

В иерархии макета есть два листа: макеты значений, которые используются для представления значений заданного размера и типа, и макеты заполнения, которые, как следует из названия, используются для представления части сегмента памяти, содержимое которой должно игнорироваться, и которые в основном присутствуют по соображениям выравнивания. Некоторые общие константы макета значений, такие как ValueLayout.JAVA_INT и ValueLayout.JAVA_FLOAT_UNALIGNED, определены в классе ValueLayout. Специальный вид макета значений, а именно макет адреса, используется для моделирования значений, обозначающих адрес области памяти.

Более сложные макеты могут быть получены из более простых: последовательный макет обозначает однородное повторение нуля или более вхождений макета элемента; макет группы обозначает гетерогенную агрегацию нуля или более макетов членов. Макеты групп бывают двух типов: структурные макеты, где макеты членов располагаются один за другим, и макеты объединений, где макеты членов располагаются с одинаковым начальным смещением.

Макеты могут быть необязательно связаны с именем. Имя макета можно использовать при построении путей макета.

Рассмотрим следующее объявление структуры в 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.carrier(). То есть константа ValueLayout.JAVA_INT имеет носитель int и размер 4 байта;
  • Размер макета адреса зависит от платформы. То есть константа ValueLayout.ADDRESS имеет размер 8 байт на 64-битной платформе;
  • Размер макета заполнения всегда задается явно при создании;
  • Размер последовательного макета, элемент которого — E, а количество элементов — L, равен размеру E, умноженному на L;
  • Размер структурного макета с макетами членов M1, M2, ..., Mn, размеры которых соответственно равны S1, S2, ..., Sn, равен S1 + S2 + ... + Sn;
  • Размер макета объединения U с макетами членов M1, M2, ..., Mn, размеры которых соответственно равны S1, S2, ..., Sn, равен max(S1, S2, ... Sn).

Кроме того, все макеты имеют естественное выравнивание (выраженное в байтах), которое определяется следующим образом:

  • Естественное выравнивание макета заполнения равно 1;
  • Естественное выравнивание макета значения, размер которого N, равно N;
  • Естественное выравнивание последовательного макета, элемент которого E, равно выравниванию E;
  • Естественное выравнивание макета группы с макетами членов M1, M2, ..., Mn, выравнивания которых соответственно равны A1, A2, ..., An, равно max(A1, A2 ... An).
Выравнивание макета может быть переопределено при необходимости (см. withByteAlignment(long)), что может быть полезно для описания макетов с более слабыми или более сильными ограничениями на выравнивание.

Пути макета

Путь макета используется для однозначного выбора макета, вложенного в какой-либо другой макет. Пути макета обычно выражаются как последовательность одного или более элементов пути. (Более формальное определение путей макета приведено ниже).

Пути макета можно использовать для:

  • получения смещений произвольно вложенных макетов;
  • получения var handle, который может использоваться для доступа к значению, соответствующему выбранному макету;
  • выбора произвольно вложенного макета.

Например, задан макет последовательности taggedValues, созданный выше. Мы можем получить смещение, в байтах, макета члена, названного value в первом элементе последовательности, следующим образом:

long valueOffset = TAGGED_VALUES.byteOffset(PathElement.sequenceElement(0),
                                          PathElement.groupElement("value")); // yields 4
Аналогично, мы можем выбрать макет члена, названного value, следующим образом:
MemoryLayout value = TAGGED_VALUES.select(PathElement.sequenceElement(),
                                         PathElement.groupElement("value"));

Открытые элементы пути

Некоторые элементы пути макета, называемые открытыми элементами пути, могут выбирать сразу несколько макетов. Например, открытые элементы пути MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(long, long) выбирают неопределенный элемент в макете последовательности. Var handle, полученный из пути макета, содержащего один или более открытых элементов пути, имеет дополнительные координаты типа long, которые могут использоваться клиентами для связывания открытых элементов в пути:
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

Открытые элементы пути также влияют на создание дескрипторов обработчиков смещений. Каждый открытый элемент пути становится дополнительным long параметром в полученном дескрипторе обработчика. Этот параметр можно использовать для указания индекса элемента последовательности, смещение которого нужно вычислить:

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

Элементы пути разыменования

Специальный тип элемента пути, называемый элементом пути разыменования, позволяет var handle, полученным из макетов памяти, следовать за указателями. Рассмотрим следующий макет:
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, макет адреса, целевым макетом которого является последовательный макет из четырех структурных макетов. Каждый структурный макет описывает двумерную точку и определен как пара или ValueLayout.JAVA_INT координат, с именами x и y соответственно.

С помощью элементов пути разыменования мы можем получить var handle, который обращается к y координате одной из точек в прямоугольнике, следующим образом:

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, также называемому начальным макетом. Каждый элемент пути макета можно рассматривать как функцию, которая обновляет текущий макет C_i-1 на некоторый другой макет C_i. То есть, для каждого элемента пути E1, E2, ... En в пути макета P, мы вычисляем C_i = f_i(C_i-1), где f_i — функция выбора, связанная с элементом пути, рассматриваемым в данный момент, обозначаемая как E_i. Конечный макет C_i также называется выбранным макетом.

Путь макета P считается корректным для начального макета C_0, если все его элементы пути E1, E2, ... En являются корректными для соответствующих входных макетов C_0, C_1, ... C_n-1. Элемент пути E считается корректным для макета L, если выполняется одно из следующих условий:

  • L является последовательным макетом, а E — элементом пути последовательности (один из MemoryLayout.PathElement.sequenceElement(long), MemoryLayout.PathElement.sequenceElement(long, long) или MemoryLayout.PathElement.sequenceElement()). Кроме того, если E содержит один или несколько индексов последовательности, эти индексы должны быть совместимы с количеством элементов последовательного макета;
  • L является макетом группы, а E — элементом пути группы (один из MemoryLayout.PathElement.groupElement(String) или MemoryLayout.PathElement.groupElement(long)). Кроме того, элемент пути группы должен ссылаться на допустимый макет члена в L, либо по имени, либо по индексу;
  • L является макетом адреса, а E — элементом пути разыменования. Кроме того, L должен определять целевой макет .
Любая попытка предоставить путь макета P, который не является корректным для начального макета C_0, приведет к исключению IllegalArgumentException.

Ограничения на режим доступа

Var handle, возвращаемый varHandle(PathElement...) или ValueLayout.varHandle(), имеет определённые характеристики доступа, которые получены из выбранного макета L:
  • Тип носителя T, полученный из L.carrier()
  • Ограничение на выравнивание A, полученное из L.byteAlignment()
  • Размер доступа S, полученный из L.byteSize()
В зависимости от вышеперечисленных характеристик, возвращаемый var handle может иметь определённые ограничения на режим доступа. Мы говорим, что var handle выровнен, если его ограничение на выравнивание A совместимо с размером доступа S, то есть если A >= S. Выровненный var handle гарантированно поддерживает следующие режимы доступа:
  • режимы чтения/записи для всех T. На 32-битных платформах поддерживаются режимы доступа get и set для long, double и MemorySegment, но они могут привести к разрыву слов, как описано в разделе 17.7 спецификации языка Java.
  • режимы атомного обновления для int, long, float, double и MemorySegment. (Будущие основные версии платформы JDK могут поддержать дополнительные типы для некоторых в настоящее время неподдерживаемых режимов доступа.)
  • числовые режимы атомного обновления для int, long и MemorySegment. (Будущие основные версии платформы JDK могут поддержать дополнительные числовые типы для некоторых в настоящее время неподдерживаемых режимов доступа.)
  • поразрядные режимы атомного обновления для int, long и MemorySegment. (Будущие основные версии платформы JDK могут поддержать дополнительные числовые типы для некоторых в настоящее время неподдерживаемых режимов доступа.)
Если T является float, double или MemorySegment, то режимы атомного обновления сравнивают значения, используя их битовую форму (см. Float.floatToRawIntBits(float), Double.doubleToRawLongBits(double) и MemorySegment.address() соответственно).

В качестве альтернативы, дескриптор переменной является невыровненным, если его ограничение выравнивания A несовместимо с размером доступа S, то есть, если A < S. Невыровненный дескриптор переменной поддерживает только режимы доступа get и set. Все остальные режимы доступа приведут к выбросу исключения UnsupportedOperationException. Кроме того, хотя и поддерживаются, режимы доступа get и set могут привести к разрыву слов.

Работа с массивами переменной длины

Мы видели, как макеты последовательностей используются для описания содержимого массива, размер которого известен статически. Однако есть случаи, когда размер массива известен только динамически. Такие массивы называются массивами переменной длины. Существуют два основных типа массивов переменной длины:
  • главный массив переменной длины, размер которого зависит от значения какой-либо несвязанной переменной или параметра;
  • массив переменной длины, вложенный в структуру, размер которого зависит от значения какого-либо другого поля во внешней структуре.
Хотя массивы переменной длины нельзя напрямую моделировать с помощью макетов последовательностей, клиенты все еще могут наслаждаться структурированным доступом к элементам массивов переменной длины с помощью дескрипторов переменных, как показано в следующих разделах.

Главные массивы переменной длины

Рассмотрим следующее объявление структуры на C:
typedef struct {
    int x;
    int y;
} Point;
В приведенном выше коде точка моделируется как две координаты (x и y соответственно). Теперь рассмотрим следующий фрагмент кода C:
int size = ...
Point *points = (Point*)malloc(sizeof(Point) * size);
for (int i = 0 ; i < size ; i++) {
   ... points[i].x ...
}
Здесь мы выделяем массив точек (points). Важно, что размер массива динамически привязан к значению переменной size. Внутри цикла осуществляется доступ к координате x всех точек в массиве.

Для моделирования этого кода на Java давайте начнем с определения макета структуры Point следующим образом:

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 последующей точки в массиве извлекается с помощью дескриптора переменной POINT_ARR_X, полученного с помощью метода arrayElementVarHandle(PathElement...). Этот дескриптор переменной содержит две координаты long: первая — смещение базового адреса (установленное на 0L), а вторая — логический индекс, который можно использовать для шага по всем элементам массива точек.

Координата смещения базового адреса позволяет клиентам выражать сложные операции доступа, вводя дополнительные вычисления смещения в дескриптор переменной (мы увидим пример этого ниже). В случаях, когда смещение базового адреса является постоянным (как в предыдущем примере), клиенты могут, при необходимости, опустить параметр смещения базового адреса и упростить выражение доступа. Это достигается с помощью адаптера дескриптора переменной MethodHandles.insertCoordinates(VarHandle, int, Object...).

Вложенные массивы переменной длины

Рассмотрим следующее объявление структуры на C:
typedef struct {
    int size;
    Point points[];
} Polygon;
В приведенном выше коде многоугольник моделируется как размер (количество сторон многоугольника) и массив точек (по одной для каждой вершины многоугольника). Количество вершин зависит от количества сторон многоугольника. Таким образом, размер массива points остается неопределенным в объявлении C, используя Гибкое член массива (функция, стандартизированная в C99).

Клиенты снова могут выполнить структурированный доступ к элементам вложенного массива переменной длины, используя метод arrayElementVarHandle(PathElement...), как показано ниже:

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 содержит макет последовательности размера ноль. Макет элемента макета последовательности — это макет POINT, показанный ранее. Макет многоугольника используется для получения дескриптора переменной, обеспечивающего доступ к размеру многоугольника, а также смещению (POINTS_OFFSET) к началу массива переменной длины points.

Координаты x всех точек в многоугольнике затем можно получить следующим образом:

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. Затем в цикле мы считываем координаты x всех точек в многоугольнике. Это делается путем предоставления пользовательского смещения (а именно, POINTS_OFFSET) к координате смещения дескриптора переменной POINT_ARR_X. Как и прежде, переменная цикла i передается как индекс дескриптора переменной POINT_ARR_X для шага по всем элементам массива переменной длины.
Требования к реализации:
Реализации этого интерфейса неизменяемы, потокобезопасны и базируются на значении.
Граф иерархии запечатанного класса:
Sealed class hierarchy graph for MemoryLayoutSealed class hierarchy graph for MemoryLayout
С:
22

Краткое описание вложенных классов

Modifier and Type Interface Description
static interface  MemoryLayout.PathElement
Элемент в пути макета.

Краткое описание методов

Modifier and Type Method Description
VarHandle arrayElementVarHandle(MemoryLayout.PathElement... elements)
Создаёт обработчик переменной, который обращается к смежным элементам в сегменте памяти по смещениям, выбранным заданным путём макета, где доступные элементы имеют этот макет, и начальный макет в пути — это макет.
long byteAlignment()
Возвращает ограничение выравнивания, связанное с этим макетом, выраженное в байтах.
long byteOffset(MemoryLayout.PathElement... elements)
Вычисляет смещение, в байтах, макета, выбранного заданным путём макета, где начальный макет в пути — это макет.
MethodHandle byteOffsetHandle(MemoryLayout.PathElement... elements)
Создаёт обработчик метода, который вычисляет смещение, в байтах, макета, выбранного заданным путём макета, где начальный макет в пути — это макет.
long byteSize()
Возвращает размер макета в байтах.
boolean equals(Object other)
Сравнивает указанный объект с этим макетом для проверки равенства.
int hashCode()
Возвращает значение хэш-кода для этого макета.
Optional<String> name()
Возвращает имя (если оно есть), связанное с этим макетом.
static PaddingLayout paddingLayout(long byteSize)
Создаёт макет заполнения заданного размера в байтах.
long scale(long offset, long index)
Возвращает offset + (byteSize() * index).
MethodHandle scaleHandle()
Возвращает обработчик метода, который может быть использован для вызова scale(long, long) для этого макета.
MemoryLayout select(MemoryLayout.PathElement... elements)
Возвращает макет, выбранный из предоставленного пути, где начальный макет в пути — это макет.
static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout)
Создаёт макет последовательности с заданным макетом элемента и количеством элементов.
MethodHandle sliceHandle(MemoryLayout.PathElement... elements)
Создаёт обработчик метода, который, принимая сегмент памяти, возвращает срез, соответствующий макету, выбранному заданным путём макета, где начальный макет в пути — это макет.
static StructLayout structLayout(MemoryLayout... elements)
Создаёт макет структуры с заданными макетами членов.
String toString()
Возвращает строковое представление этого макета.
static UnionLayout unionLayout(MemoryLayout... elements)
Создаёт макет объединения с заданными макетами членов.
VarHandle varHandle(MemoryLayout.PathElement... elements)
Создаёт обработчик переменной, который обращается к сегменту памяти по смещению, выбранному заданным путём макета, где начальный макет в пути — это макет.
MemoryLayout withByteAlignment(long byteAlignment)
Возвращает макет памяти с такими же характеристиками, что и у этого макета, но с заданным ограничением выравнивания (в байтах).
MemoryLayout withName(String name)
Возвращает макет памяти с такими же характеристиками, что и у этого макета, но с заданным именем.
MemoryLayout withoutName()
Возвращает макет памяти с такими же характеристиками, что и у этого макета, но без имени.

Подробное описание методов

byteSize

long byteSize()
Возвращает размер макета в байтах.
Возвращает:
размер макета в байтах

name

Optional<String> name()
Возвращает имя (если есть) этого макета.
Возвращает:
имя (если есть) этого макета
См. также:
  • withName(String)

withName

MemoryLayout withName(String name)
Возвращает макет памяти с теми же характеристиками, что и этот макет, но с указанным именем.
Параметры:
name - имя макета
Возвращает:
макет памяти с теми же характеристиками, что и этот макет, но с указанным именем
См. также:
  • name()

withoutName

MemoryLayout withoutName()
Возвращает макет памяти с теми же характеристиками, что и этот макет, но без имени.
Примечание API:
Это может быть полезно для сравнения двух макетов, имеющих разные имена, но в остальном равных.
Возвращает:
макет памяти с теми же характеристиками, что и этот макет, но без имени
См. также:
  • name()

byteAlignment

long byteAlignment()
Возвращает ограничение выравнивания, связанное с этим макетом, выраженное в байтах.

Выравнивание макета определяет степень двойки A, которая является байтовым выравниванием макета, где A — это количество байтов, которые должны быть выровнены для любого указателя, правильно указывающего на этот макет. Таким образом:

  • A=1 означает отсутствие выравнивания (в обычном понимании), что часто используется в пакетах.
  • A=8 означает выравнивание по слову (на LP64), A=4 выравнивание по целому числу, A=2 выравнивание по короткому числу и т. д.
  • A=64 — это самое строгое требование выравнивания, накладываемое ABI x86/SV (для данных AVX-512).
Если для этого макета не было задано явного ограничения выравнивания (см. withByteAlignment(long)), то этот метод возвращает ограничение естественного выравнивания (в байтах), связанное с этим макетом.
Возвращает:
ограничение выравнивания, связанное с этим макетом, выраженное в байтах

withByteAlignment

MemoryLayout withByteAlignment(long byteAlignment)
Возвращает макет памяти с теми же характеристиками, что и этот макет, но с заданным ограничением выравнивания (в байтах).
Параметры:
byteAlignment - ограничение выравнивания макета, выраженное в байтах
Возвращает:
макет памяти с теми же характеристиками, что и этот макет, но с заданным ограничением выравнивания (в байтах)
Исключения:
IllegalArgumentException - если byteAlignment не является степенью двойки

scale

long scale(long offset, long index)
Возвращает offset + (byteSize() * index).
Параметры:
offset - базовый смещение
index - индекс, который будет масштабироваться с размером этого макета в байтах
Возвращает:
offset + (byteSize() * index)
Исключения:
IllegalArgumentException - если offset или index отрицательны
ArithmeticException - если сложение или умножение переполняет

scaleHandle

MethodHandle scaleHandle()
Возвращает дескриптор метода, который можно использовать для вызова scale(long, long) для этого макета.
Возвращает:
дескриптор метода, который можно использовать для вызова scale(long, long) для этого макета

byteOffset

long byteOffset(MemoryLayout.PathElement... elements)
Вычисляет смещение в байтах выбранного макета, заданного путем макета, где начальный макет в пути — это этот макет.
Параметры:
elements - элементы пути макета
Возвращает:
Смещение в байтах выбранного макета путем макета в elements
Исключения:
IllegalArgumentException - если путь макета не корректен для этого макета
IllegalArgumentException - если путь макета содержит один или несколько элементов пути для открытия
IllegalArgumentException - если путь макета содержит один или несколько элементов пути для ссылки

byteOffsetHandle

MethodHandle byteOffsetHandle(MemoryLayout.PathElement... elements)
Создает дескриптор метода, который вычисляет смещение в байтах выбранного макета, заданного путем макета, где начальный макет в пути — это этот макет.

Возвращаемый дескриптор метода обладает следующими характеристиками:

  • его тип возвращаемого значения — long;
  • у него есть один ведущий long параметр, представляющий базовое смещение;
  • у него есть нуль или более последующих параметров типа long, по одному на каждый элемент пути для открытия в предоставленном пути макета. Порядок этих параметров соответствует порядку, в котором элементы пути для открытия появляются в предоставленном пути макета.

Конечное смещение, возвращаемое дескриптором метода, вычисляется следующим образом:


 offset = b + c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
 
где b представляет базовое смещение, предоставленное в качестве динамического long аргумента, x_1, x_2, ... x_n представляют индексы в последовательностях, предоставленных в качестве динамических long аргументов, тогда как s_1, s_2, ... s_n — это статические константы шага, полученные из размера макета элемента последовательности, а c_1, c_2, ... c_m — это другие статические константы смещения (например, смещения полей), которые получены из пути макета.

Для любого заданного динамического аргумента x_i, он должен быть таким, что 0 <= x_i < size_i, где size_i — это размер элемента пути для открытия, связанного с x_i. В противном случае возвращаемый дескриптор метода генерирует исключение IndexOutOfBoundsException. Кроме того, значение b должно быть таким, чтобы вычисление offset не переполняло, иначе возвращаемый дескриптор метода генерирует исключение ArithmeticException.

Примечание API:
Возвращаемый дескриптор метода можно использовать для вычисления смещения макета, аналогично byteOffset(PathElement...), но более гибко, так как некоторые индексы могут быть указаны при вызове дескриптора метода.
Параметры:
elements - элементы пути макета
Возвращает:
дескриптор метода, который вычисляет смещение в байтах выбранного макета по заданному пути макета
Исключения:
IllegalArgumentException - если путь макета не корректен для этого макета
IllegalArgumentException - если путь макета содержит один или несколько элементов пути для ссылки

varHandle

VarHandle varHandle(MemoryLayout.PathElement... elements)
Создаёт обработчик var, который обращается к сегменту памяти по смещению, выбранному заданным путём расположения, где начальное расположение в пути — это данное расположение.

Возвращаемый обработчик var имеет следующие характеристики:

  • его тип получен из носителя выбранного расположения значения;
  • он имеет ведущий параметр типа MemorySegment, представляющий доступный сегмент;
  • последующий long параметр, соответствующий базовому смещению, обозначенному как B;
  • он имеет ноль или более следующих координат доступа типа long, по одной для каждого элемента открытого пути в предоставленном пути расположения, обозначенных как I1, I2, ... In, соответственно. Порядок этих координат доступа соответствует порядку, в котором элементы открытого пути встречаются в предоставленном пути расположения.

Если предоставленный путь расположения P не содержит элементов разыменования, то смещение O операции доступа вычисляется следующим образом:

O = this.byteOffsetHandle(P).invokeExact(B, I1, I2, ... In);

Доступ к сегменту памяти с помощью обработчика var, возвращаемого этим методом, осуществляется с проверками:

  • Физический адрес доступного сегмента памяти должен быть выровнен в соответствии с ограничением выравнивания корневого расположения (это расположение), иначе выбрасывается IllegalArgumentException. Обратите внимание, что ограничение выравнивания корневого расположения может быть более строгим (но не менее строгим), чем ограничение выравнивания выбранного расположения значения.
  • Операция доступа должна находиться внутри пространственных границ доступного сегмента памяти, иначе выбрасывается IndexOutOfBoundsException. Это имеет место, когда B + A <= S, где B — базовое смещение (определено выше), A — размер этого расположения, а S — размер доступного сегмента памяти. Обратите внимание, что размер этого расположения может быть больше, чем размер доступного расположения (например, при доступе к члену структуры).
  • Если предоставленный путь расположения имеет элемент открытого пути, размер которого равен S, значение соответствующей координаты доступа long I должно быть 0 <= I < S, иначе выбрасывается IndexOutOfBoundsException.
  • Доступный сегмент памяти должен быть доступен потоку, выполняющему операцию доступа, иначе выбрасывается WrongThreadException.
  • Для операций записи доступный сегмент памяти не должен быть только для чтения, иначе выбрасывается IllegalArgumentException.
  • Область действия, связанная с доступным сегментом, должна быть активна, иначе выбрасывается IllegalStateException.

Если выбранное расположение является расположением адреса, вызов VarHandle.get(Object...) на возвращаемом обработчике var вернёт новый сегмент памяти. Сегмент связан со глобальной областью. Кроме того, размер сегмента зависит от того, имеет ли расположение адреса целевое расположение. Точнее:

  • Если расположение адреса имеет целевое расположение T, то размер возвращаемого сегмента равен T.byteSize();
  • В противном случае расположение адреса не имеет целевого расположения, и размер возвращаемого сегмента равен нулю.
Кроме того, если выбранное расположение является расположением адреса, вызов VarHandle.set(Object...) может вызвать IllegalArgumentException, если сегмент памяти, представляющий адрес для записи, не является нативным сегментом памяти.

Если предоставленный путь расположения имеет размер m и содержит элемент пути разыменования в позиции k (где k <= m), тогда выводятся два пути расположения P и Q, где P содержит все элементы пути от 0 до k - 1, а Q содержит все элементы пути от k + 1 до m (Q может быть пустым путём расположения, если k == m). Затем возвращаемый обработчик var вычисляется следующим образом:

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));
(Вышеизложенное можно тривиально обобщить на случаи, когда предоставленный путь расположения содержит более одного элемента пути разыменования).

Например, рассмотрим расположение памяти, выраженное экземпляром GroupLayout, созданным следующим образом:

    GroupLayout grp = java.lang.foreign.MemoryLayout.structLayout(
            MemoryLayout.paddingLayout(4),
            ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
    );
Для доступа к расположению элемента, названному value, мы можем создать обработчик var следующим образом:
    VarHandle handle = grp.varHandle(PathElement.groupElement("value")); //(MemorySegment, long) -> int
Примечание API:
Полученный обработчик var имеет определённые ограничения режима доступа, которые характерны для всех обработчиков var, полученных из расположений памяти.
Параметры:
elements - элементы пути расположения
Возвращает:
обработчик var, который обращается к сегменту памяти по смещению, выбранному заданным путём расположения
Выбрасывает:
IllegalArgumentException - если путь расположения не правильно сформирован для этого расположения
IllegalArgumentException - если расположение, выбранное предоставленным путём, не является расположением значения

arrayElementVarHandle

VarHandle arrayElementVarHandle(MemoryLayout.PathElement... elements)
Создаёт обработчик переменной, который обращается к смежным элементам в сегменте памяти по смещениям, выбранным заданным путём компоновки, где доступные элементы имеют эту компоновку, и где начальная компоновка в пути — эта компоновка.

Возвращаемый обработчик переменной имеет следующие характеристики:

  • его тип выводится из носителя выбранной компоновки значения;
  • он имеет ведущий параметр типа MemorySegment, представляющий доступный сегмент
  • последующий long параметр, соответствующий базовому смещению, обозначенному как B;
  • последующий long параметр, соответствующий индексу массива, обозначенный как I0. Индекс массива используется для масштабирования смещения доступа размером этой компоновки;
  • он имеет ноль или более хвостовых координат доступа типа long, по одной для каждого открытого элемента пути в предоставленном пути компоновки, обозначенном как I1, I2, ... In, соответственно. Порядок этих координат доступа соответствует порядку, в котором открытые элементы пути встречаются в предоставленном пути компоновки.

Если предоставленный путь компоновки P не содержит элементов разыменования, то смещение O операции доступа вычисляется следующим образом:

O = this.byteOffsetHandle(P).invokeExact(this.scale(B, I0), I1, I2, ... In);

Более формально, обработчик метода, возвращаемый этим методом, получается из varHandle(PathElement...), следующим образом:

MethodHandles.collectCoordinates(varHandle(elements), 1, scaleHandle())

Доступ к сегменту памяти с помощью обработчика переменной, возвращаемого этим методом, подлежит следующим проверкам:

  • Физический адрес доступного сегмента памяти должен быть выровнен в соответствии с ограничением выравнивания корневой компоновки (этой компоновки), иначе будет брошено исключение IllegalArgumentException. Обратите внимание, что ограничение выравнивания корневой компоновки может быть более строгим (но не менее), чем ограничение выравнивания выбранной компоновки значения.
  • Операция доступа должна находиться внутри пространственных границ доступного сегмента памяти, иначе будет брошено исключение IndexOutOfBoundsException. Это происходит, когда B + A <= S, где B — базовое смещение (определено выше), A — размер этой компоновки, а S — размер доступного сегмента памяти. Обратите внимание, что размер этой компоновки может быть больше, чем размер доступной компоновки (например, при доступе к члену структуры).
  • Если предоставленный путь компоновки имеет открытый элемент пути, размер которого равен S, соответствующее хвостовое long значение координаты I должно быть равно 0 <= I < S, иначе будет брошено исключение IndexOutOfBoundsException.
  • Доступный сегмент памяти должен быть доступен для потока, выполняющего операцию доступа, иначе будет брошено исключение WrongThreadException.
  • Для операций записи доступный сегмент памяти не должен быть только для чтения, иначе будет брошено исключение IllegalArgumentException.
  • Область охватом, связанная с доступным сегментом, должна быть активна, иначе будет брошено исключение IllegalStateException.
API Note:
Так как ведущая координата индекса I0 не ограничена никакой компоновкой последовательности, она может принимать любое неотрицательное значение — при условии, что вычисление смещения не переполнится или вычисленное смещение не выйдет за пределы пространственных границ доступного сегмента памяти. В связи с этим обработчики переменных, возвращаемые этим методом, могут быть особенно полезны при доступе к массивам переменной длины.
Parameters:
elements - элементы пути компоновки
Returns:
обработчик переменной, который обращается к смежным элементам в сегменте памяти по смещениям, выбранным заданным путём компоновки
Throws:
IllegalArgumentException - если путь компоновки не корректен для этой компоновки
IllegalArgumentException - если компоновка, выбранная предоставленным путём, не является компоновкой значения

sliceHandle

MethodHandle sliceHandle(MemoryLayout.PathElement... elements)
Создаёт обработчик метода, который, получив сегмент памяти, возвращает срез, соответствующий компоновке, выбранной заданным путём компоновки, где начальная компоновка в пути — эта компоновка.

Возвращаемый обработчик метода имеет следующие характеристики:

  • его тип возвращаемого значения — MemorySegment;
  • он имеет ведущий параметр типа MemorySegment, соответствующий сегменту памяти, который нужно нарезать
  • последующий long параметр, соответствующий базовому смещению
  • он имеет ноль или более хвостовых параметров типа long, по одному для каждого открытого элемента пути в предоставленном пути компоновки. Порядок этих параметров соответствует порядку, в котором открытые элементы пути встречаются в предоставленном пути компоновки.

Смещение O возвращаемого сегмента вычисляется так же, как и при вызове обработчика смещения байта, созданного с помощью предоставленных элементов пути.

Вычисление среза сегмента памяти с помощью обработчика метода, возвращаемого этим методом, подлежит следующим проверкам:

  • Физический адрес доступного сегмента памяти должен быть выровнен в соответствии с ограничением выравнивания корневой компоновки (этой компоновки), иначе будет вызвано исключение IllegalArgumentException. Обратите внимание, что ограничение выравнивания корневой компоновки может быть более строгим (но не менее), чем ограничение выравнивания выбранной компоновки.
  • Операция нарезки должна находиться внутри пространственных границ доступного сегмента памяти, иначе будет брошено исключение IndexOutOfBoundsException. Это происходит, когда B + A <= S, где B — базовое смещение (определено выше), A — размер этой компоновки, а S — размер доступного сегмента памяти. Обратите внимание, что размер этой компоновки может быть больше, чем размер доступной компоновки (например, при доступе к члену структуры).
  • Если предоставленный путь компоновки имеет открытый элемент пути, размер которого равен S, соответствующее хвостовое long значение координаты I должно быть равно 0 <= I < S, иначе будет брошено исключение IndexOutOfBoundsException.
API Note:
Возвращаемый обработчик метода может использоваться для получения среза сегмента памяти, аналогично MemorySegment.asSlice(long, long), но более гибко, так как некоторые индексы могут быть указаны при вызове обработчика метода.
Parameters:
elements - элементы пути компоновки
Returns:
обработчик метода, используемый для нарезки сегмента памяти по смещению, выбранному заданным путём компоновки
Throws:
IllegalArgumentException - если путь компоновки не корректен для этой компоновки
IllegalArgumentException - если путь компоновки содержит один или несколько элементов пути разыменования

select

MemoryLayout select(MemoryLayout.PathElement... elements)
Возвращает компоновку, выбранную из предоставленного пути, где начальная компоновка в пути — эта компоновка.
Parameters:
elements - элементы пути компоновки
Returns:
компоновка, выбранная путём компоновки в elements
Throws:
IllegalArgumentException - если путь компоновки не корректен для этой компоновки
IllegalArgumentException - если путь компоновки содержит один или несколько элементов пути разыменования
IllegalArgumentException - если путь компоновки содержит один или несколько элементов пути, которые выбирают один или несколько индексов последовательности, таких как MemoryLayout.PathElement.sequenceElement(long) и MemoryLayout.PathElement.sequenceElement(long, long))

equals

boolean equals(Object other)
Сравнивает указанный объект с этим макетом на предмет равенства. Возвращает true только в том случае, если указанный объект также является макетом и равен этому макету. Два макета считаются равными, если они одного типа, имеют одинаковый размер, имя и ограничение выравнивания. Кроме того, в зависимости от типа макета должны быть выполнены дополнительные условия:
  • два макета значений считаются равными, если у них одинаковый порядок и носитель. Кроме того, два макета адресов считаются равными, если у них также одинаковый целевой макет;
  • два макета последовательностей считаются равными, если у них одинаковое количество элементов (см. SequenceLayout.elementCount()), и если их макеты элементов (см. SequenceLayout.elementLayout()) также равны;
  • два макета групп считаются равными, если они одного типа (см. StructLayout, UnionLayout) и если их макеты членов (см. GroupLayout.memberLayouts()) также равны.
Overrides:
equals в классе Object
Parameters:
other - объект, подлежащий сравнению на равенство с этим макетом
Returns:
true, если указанный объект равен этому макету
See Also:
  • Object.hashCode()
  • HashMap

hashCode

int hashCode()
Возвращает значение хэш-кода для этого макета.
Overrides:
hashCode в классе Object
Returns:
значение хэш-кода для этого макета
See Also:
  • Object.equals(java.lang.Object)
  • System.identityHashCode(java.lang.Object)

toString

String toString()
Возвращает строковое представление этого макета.
Overrides:
toString в классе Object
Returns:
строковое представление этого макета

paddingLayout

static PaddingLayout paddingLayout(long byteSize)
Создаёт макет заполнения заданного размера в байтах. Ограничение выравнивания полученного макета равно 1. Таким образом, независимо от его размера, в отсутствие явного ограничения выравнивания, макет заполнения не влияет на естественное выравнивание макета группы или последовательности, в который он вложен.
Parameters:
byteSize - размер заполнения (выраженный в байтах)
Returns:
новый макет селектора
Throws:
IllegalArgumentException - если byteSize <= 0

sequenceLayout

static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout)
Создаёт макет последовательности с заданным макетом элемента и количеством элементов.
Parameters:
elementCount - количество элементов последовательности
elementLayout - макет элемента последовательности
Returns:
новый макет последовательности с заданным макетом элемента и размером
Throws:
IllegalArgumentException - если elementCount отрицательное
IllegalArgumentException - если elementLayout.byteSize() * elementCount переполняется
IllegalArgumentException - если elementLayout.byteSize() % elementLayout.byteAlignment() != 0

structLayout

static StructLayout structLayout(MemoryLayout... elements)
Создаёт макет структуры с заданными макетами членов.
API Note:
Этот фабричный метод не автоматически выравнивает макеты элементов, вставляя дополнительные макеты заполнения. Таким образом, следующее создание макета структуры завершится исключением:
structLayout(JAVA_SHORT, JAVA_INT);
Чтобы избежать исключения, клиенты могут либо вставить дополнительные макеты заполнения:
structLayout(JAVA_SHORT, MemoryLayout.paddingLayout(2), JAVA_INT);
Или же, альтернативно, они могут использовать макет члена с меньшим ограничением выравнивания. Это приведёт к упакованному макету структуры:
structLayout(JAVA_SHORT, JAVA_INT.withByteAlignment(2));
Parameters:
elements - макеты членов макета структуры
Returns:
макет структуры с заданными макетами членов
Throws:
IllegalArgumentException - если сумма размеров в байтах макетов членов переполняется
IllegalArgumentException - если макет члена в elements встречается в смещении (относительно начала макета структуры), которое несовместимо с его ограничением выравнивания

unionLayout

static UnionLayout unionLayout(MemoryLayout... elements)
Создаёт макет объединения с заданными макетами членов.
Parameters:
elements - макеты членов макета объединения
Returns:
макет объединения с заданными макетами членов

© 1993, 2025, 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://download.java.net/java/early_access/jdk24/docs/api/java.base/java/lang/foreign/MemoryLayout.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API