9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 *
25 */
26 package jdk.internal.foreign;
27
28 import jdk.internal.vm.annotation.ForceInline;
29
30 import java.lang.foreign.AddressLayout;
31 import java.lang.foreign.GroupLayout;
32 import java.lang.foreign.MemoryLayout;
33 import java.lang.foreign.MemorySegment;
34 import java.lang.foreign.SequenceLayout;
35 import java.lang.foreign.StructLayout;
36 import java.lang.foreign.ValueLayout;
37 import java.lang.invoke.MethodHandle;
38 import java.lang.invoke.MethodHandles;
39 import java.lang.invoke.MethodType;
40 import java.lang.invoke.VarHandle;
41 import java.util.Arrays;
42 import java.util.Objects;
43 import java.util.function.UnaryOperator;
44
45 /**
46 * This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)},
47 * a path can be constructed by selecting layout elements using the selector methods provided by this class
48 * (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}).
49 * Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected
50 * by the path (see {@link #offset}), or obtain var handle to access the selected layout element
51 * given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle()}).
52 */
53 public class LayoutPath {
54
55 private static final long[] EMPTY_STRIDES = new long[0];
56 private static final long[] EMPTY_BOUNDS = new long[0];
57 private static final MethodHandle[] EMPTY_DEREF_HANDLES = new MethodHandle[0];
58
59 private static final MethodHandle MH_ADD_SCALED_OFFSET;
60 private static final MethodHandle MH_SLICE;
61 private static final MethodHandle MH_SLICE_LAYOUT;
62 private static final MethodHandle MH_CHECK_ALIGN;
63 private static final MethodHandle MH_SEGMENT_RESIZE;
64
65 static {
66 try {
67 MethodHandles.Lookup lookup = MethodHandles.lookup();
68 MH_ADD_SCALED_OFFSET = lookup.findStatic(LayoutPath.class, "addScaledOffset",
69 MethodType.methodType(long.class, long.class, long.class, long.class, long.class));
70 MH_SLICE = lookup.findVirtual(MemorySegment.class, "asSlice",
71 MethodType.methodType(MemorySegment.class, long.class, long.class));
72 MH_SLICE_LAYOUT = lookup.findVirtual(MemorySegment.class, "asSlice",
73 MethodType.methodType(MemorySegment.class, long.class, MemoryLayout.class));
74 MH_CHECK_ALIGN = lookup.findStatic(LayoutPath.class, "checkAlign",
75 MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class));
76 MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment",
77 MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class));
78 } catch (Throwable ex) {
79 throw new ExceptionInInitializerError(ex);
80 }
81 }
82
83 private final MemoryLayout layout;
84 private final long offset;
85 private final LayoutPath enclosing;
86 private final long[] strides;
87
88 private final long[] bounds;
89 private final MethodHandle[] derefAdapters;
90
91 private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath enclosing) {
92 this.layout = layout;
93 this.offset = offset;
94 this.strides = strides;
95 this.bounds = bounds;
96 this.derefAdapters = derefAdapters;
97 this.enclosing = enclosing;
98 }
99
100 // Layout path selector methods
101
102 public LayoutPath sequenceElement() {
103 check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
104 SequenceLayout seq = (SequenceLayout)layout;
105 MemoryLayout elem = seq.elementLayout();
106 return LayoutPath.nestedPath(elem, offset, addStride(elem.byteSize()), addBound(seq.elementCount()), derefAdapters, this);
107 }
108
109 public LayoutPath sequenceElement(long start, long step) {
110 check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
111 SequenceLayout seq = (SequenceLayout)layout;
112 checkSequenceBounds(seq, start);
113 MemoryLayout elem = seq.elementLayout();
114 long elemSize = elem.byteSize();
115 long nelems = step > 0 ?
116 seq.elementCount() - start :
117 start + 1;
118 long maxIndex = Math.ceilDiv(nelems, Math.abs(step));
119 return LayoutPath.nestedPath(elem, offset + (start * elemSize),
120 addStride(elemSize * step), addBound(maxIndex), derefAdapters, this);
121 }
122
123 public LayoutPath sequenceElement(long index) {
124 check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
125 SequenceLayout seq = (SequenceLayout)layout;
126 checkSequenceBounds(seq, index);
127 long elemSize = seq.elementLayout().byteSize();
128 long elemOffset = elemSize * index;
129 return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters,this);
130 }
131
132 public LayoutPath groupElement(String name) {
133 check(GroupLayout.class, "attempting to select a group element from a non-group layout");
134 GroupLayout g = (GroupLayout)layout;
135 long offset = 0;
136 MemoryLayout elem = null;
137 for (int i = 0; i < g.memberLayouts().size(); i++) {
138 MemoryLayout l = g.memberLayouts().get(i);
139 if (l.name().isPresent() &&
140 l.name().get().equals(name)) {
141 elem = l;
142 break;
143 } else if (g instanceof StructLayout) {
144 offset += l.byteSize();
145 }
146 }
147 if (elem == null) {
148 throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
149 }
150 return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
151 }
152
153 public LayoutPath groupElement(long index) {
154 check(GroupLayout.class, "attempting to select a group element from a non-group layout");
155 GroupLayout g = (GroupLayout)layout;
156 long elemSize = g.memberLayouts().size();
157 long offset = 0;
158 MemoryLayout elem = null;
159 for (int i = 0; i <= index; i++) {
160 if (i == elemSize) {
161 throw badLayoutPath("cannot resolve element " + index + " in layout " + layout);
162 }
163 elem = g.memberLayouts().get(i);
164 if (g instanceof StructLayout && i < index) {
165 offset += elem.byteSize();
166 }
167 }
168 return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
169 }
170
171 public LayoutPath derefElement() {
172 if (!(layout instanceof AddressLayout addressLayout) ||
173 addressLayout.targetLayout().isEmpty()) {
174 throw badLayoutPath("Cannot dereference layout: " + layout);
175 }
176 MemoryLayout derefLayout = addressLayout.targetLayout().get();
177 MethodHandle handle = dereferenceHandle(false).toMethodHandle(VarHandle.AccessMode.GET);
178 handle = MethodHandles.filterReturnValue(handle,
179 MethodHandles.insertArguments(MH_SEGMENT_RESIZE, 1, derefLayout));
180 return derefPath(derefLayout, handle, this);
181 }
182
183 private static MemorySegment resizeSegment(MemorySegment segment, MemoryLayout layout) {
184 return Utils.longToAddress(segment.address(), layout.byteSize(), layout.byteAlignment());
185 }
186
187 // Layout path projections
188
189 public long offset() {
190 return offset;
191 }
192
193 public VarHandle dereferenceHandle() {
194 return dereferenceHandle(true);
195 }
196
197 public VarHandle dereferenceHandle(boolean adapt) {
198 if (!(layout instanceof ValueLayout valueLayout)) {
199 throw new IllegalArgumentException("Path does not select a value layout");
200 }
201
202 // If we have an enclosing layout, drop the alignment check for the accessed element,
203 // we check the root layout instead
204 ValueLayout accessedLayout = enclosing != null ? valueLayout.withByteAlignment(1) : valueLayout;
205 VarHandle handle = Utils.makeSegmentViewVarHandle(accessedLayout);
206 handle = MethodHandles.collectCoordinates(handle, 1, offsetHandle());
207
208 // we only have to check the alignment of the root layout for the first dereference we do,
209 // as each dereference checks the alignment of the target address when constructing its segment
210 // (see Utils::longToAddress)
211 if (derefAdapters.length == 0 && enclosing != null) {
212 MethodHandle checkHandle = MethodHandles.insertArguments(MH_CHECK_ALIGN, 1, rootLayout());
213 handle = MethodHandles.filterCoordinates(handle, 0, checkHandle);
214 }
215
216 if (adapt) {
217 for (int i = derefAdapters.length; i > 0; i--) {
218 handle = MethodHandles.collectCoordinates(handle, 0, derefAdapters[i - 1]);
219 }
220 }
221 return handle;
222 }
223
224 @ForceInline
225 private static long addScaledOffset(long base, long index, long stride, long bound) {
226 Objects.checkIndex(index, bound);
227 return base + (stride * index);
228 }
229
230 public MethodHandle offsetHandle() {
231 MethodHandle mh = MethodHandles.identity(long.class);
232 for (int i = strides.length - 1; i >=0; i--) {
233 MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, strides[i], bounds[i]);
234 // (J, ...) -> J to (J, J, ...) -> J
235 // i.e. new coord is prefixed. Last coord will correspond to innermost layout
236 mh = MethodHandles.collectArguments(mh, 0, collector);
237 }
238 mh = MethodHandles.insertArguments(mh, 0, offset);
239 return mh;
240 }
241
242 private MemoryLayout rootLayout() {
243 return enclosing != null ? enclosing.rootLayout() : this.layout;
244 }
245
246 public MethodHandle sliceHandle() {
247 MethodHandle sliceHandle;
248 if (enclosing != null) {
249 // drop the alignment check for the accessed element, we check the root layout instead
250 sliceHandle = MH_SLICE; // (MS, long, long) -> MS
251 sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout.byteSize()); // (MS, long) -> MS
252 } else {
253 sliceHandle = MH_SLICE_LAYOUT; // (MS, long, MemoryLayout) -> MS
254 sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout); // (MS, long) -> MS
255 }
256 sliceHandle = MethodHandles.collectArguments(sliceHandle, 1, offsetHandle()); // (MS, ...) -> MS
257
258 if (enclosing != null) {
259 MethodHandle checkHandle = MethodHandles.insertArguments(MH_CHECK_ALIGN, 1, rootLayout());
260 sliceHandle = MethodHandles.filterArguments(sliceHandle, 0, checkHandle);
261 }
262
263 return sliceHandle;
264 }
265
266 private static MemorySegment checkAlign(MemorySegment segment, MemoryLayout constraint) {
267 if (!((AbstractMemorySegmentImpl) segment).isAlignedForElement(0, constraint)) {
268 throw new IllegalArgumentException("Target offset incompatible with alignment constraints: " + constraint.byteAlignment());
269 }
270 return segment;
271 }
272
273 public MemoryLayout layout() {
274 return layout;
275 }
276
277 // Layout path construction
278
279 public static LayoutPath rootPath(MemoryLayout layout) {
280 return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, EMPTY_DEREF_HANDLES, null);
281 }
282
283 private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath encl) {
284 return new LayoutPath(layout, offset, strides, bounds, derefAdapters, encl);
285 }
286
287 private static LayoutPath derefPath(MemoryLayout layout, MethodHandle handle, LayoutPath encl) {
288 MethodHandle[] handles = Arrays.copyOf(encl.derefAdapters, encl.derefAdapters.length + 1);
289 handles[encl.derefAdapters.length] = handle;
290 return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, handles, null);
291 }
292
293 // Helper methods
294
295 private void check(Class<?> layoutClass, String msg) {
296 if (!layoutClass.isAssignableFrom(layout.getClass())) {
297 throw badLayoutPath(msg);
298 }
299 }
300
301 private void checkSequenceBounds(SequenceLayout seq, long index) {
302 if (index >= seq.elementCount()) {
303 throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount()));
304 }
305 }
306
307 private static IllegalArgumentException badLayoutPath(String cause) {
308 return new IllegalArgumentException("Bad layout path: " + cause);
309 }
310
311 private long[] addStride(long stride) {
312 long[] newStrides = Arrays.copyOf(strides, strides.length + 1);
313 newStrides[strides.length] = stride;
314 return newStrides;
315 }
316
317 private long[] addBound(long maxIndex) {
318 long[] newBounds = Arrays.copyOf(bounds, bounds.length + 1);
319 newBounds[bounds.length] = maxIndex;
320 return newBounds;
321 }
322
323 /**
324 * This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
325 * is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
326 */
327 public static final class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
328
329 public enum PathKind {
330 SEQUENCE_ELEMENT("unbound sequence element"),
331 SEQUENCE_ELEMENT_INDEX("bound sequence element"),
332 SEQUENCE_RANGE("sequence range"),
333 GROUP_ELEMENT("group element"),
334 DEREF_ELEMENT("dereference element");
335
336 final String description;
337
338 PathKind(String description) {
339 this.description = description;
340 }
341
342 public String description() {
|
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 *
25 */
26 package jdk.internal.foreign;
27
28 import jdk.internal.vm.annotation.ForceInline;
29 import java.lang.foreign.AddressLayout;
30 import java.lang.foreign.GroupLayout;
31 import java.lang.foreign.MemoryLayout;
32 import java.lang.foreign.MemorySegment;
33 import java.lang.foreign.SequenceLayout;
34 import java.lang.foreign.StructLayout;
35 import java.lang.foreign.ValueLayout;
36 import java.lang.invoke.MethodHandle;
37 import java.lang.invoke.MethodHandles;
38 import java.lang.invoke.MethodType;
39 import java.lang.invoke.VarHandle;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.Objects;
43 import java.util.function.UnaryOperator;
44 import java.util.stream.IntStream;
45 import java.util.stream.Stream;
46
47 import static java.util.stream.Collectors.joining;
48
49 /**
50 * This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)}),
51 * a path can be constructed by selecting layout elements using the selector methods provided by this class
52 * (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}).
53 * Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected
54 * by the path (see {@link #offset}), or obtain var handle to access the selected layout element
55 * given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle()}).
56 */
57 public class LayoutPath {
58
59 private static final long[] EMPTY_STRIDES = new long[0];
60 private static final long[] EMPTY_BOUNDS = new long[0];
61 private static final MethodHandle[] EMPTY_DEREF_HANDLES = new MethodHandle[0];
62
63 private static final MethodHandle MH_ADD_SCALED_OFFSET;
64 private static final MethodHandle MH_SLICE;
65 private static final MethodHandle MH_SLICE_LAYOUT;
66 private static final MethodHandle MH_CHECK_ALIGN;
67 private static final MethodHandle MH_SEGMENT_RESIZE;
68 private static final MethodHandle MH_ADD;
69
70 static {
71 try {
72 MethodHandles.Lookup lookup = MethodHandles.lookup();
73 MH_ADD_SCALED_OFFSET = lookup.findStatic(LayoutPath.class, "addScaledOffset",
74 MethodType.methodType(long.class, long.class, long.class, long.class, long.class));
75 MH_SLICE = lookup.findVirtual(MemorySegment.class, "asSlice",
76 MethodType.methodType(MemorySegment.class, long.class, long.class));
77 MH_SLICE_LAYOUT = lookup.findVirtual(MemorySegment.class, "asSlice",
78 MethodType.methodType(MemorySegment.class, long.class, MemoryLayout.class));
79 MH_CHECK_ALIGN = lookup.findStatic(LayoutPath.class, "checkAlign",
80 MethodType.methodType(void.class, MemorySegment.class, long.class, MemoryLayout.class));
81 MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment",
82 MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class));
83 MH_ADD = lookup.findStatic(Long.class, "sum",
84 MethodType.methodType(long.class, long.class, long.class));
85 } catch (Throwable ex) {
86 throw new ExceptionInInitializerError(ex);
87 }
88 }
89
90 private final MemoryLayout layout;
91 private final long offset;
92 private final LayoutPath enclosing;
93 private final long[] strides;
94 private final long[] bounds;
95 private final MethodHandle[] derefAdapters;
96
97 private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath enclosing) {
98 this.layout = layout;
99 this.offset = offset;
100 this.strides = strides;
101 this.bounds = bounds;
102 this.derefAdapters = derefAdapters;
103 this.enclosing = enclosing;
104 }
105
106 // Layout path selector methods
107
108 public LayoutPath sequenceElement() {
109 SequenceLayout seq = requireSequenceLayout();
110 MemoryLayout elem = seq.elementLayout();
111 return LayoutPath.nestedPath(elem, offset, addStride(elem.byteSize()), addBound(seq.elementCount()), derefAdapters, this);
112 }
113
114 public LayoutPath sequenceElement(long start, long step) {
115 SequenceLayout seq = requireSequenceLayout();
116 checkSequenceBounds(seq, start);
117 MemoryLayout elem = seq.elementLayout();
118 long elemSize = elem.byteSize();
119 long nelems = step > 0 ?
120 seq.elementCount() - start :
121 start + 1;
122 long maxIndex = Math.ceilDiv(nelems, Math.abs(step));
123 return LayoutPath.nestedPath(elem, offset + (start * elemSize),
124 addStride(elemSize * step), addBound(maxIndex), derefAdapters, this);
125 }
126
127 public LayoutPath sequenceElement(long index) {
128 SequenceLayout seq = requireSequenceLayout();
129 checkSequenceBounds(seq, index);
130 long elemSize = seq.elementLayout().byteSize();
131 long elemOffset = elemSize * index;
132 return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters, this);
133 }
134
135 public LayoutPath groupElement(String name) {
136 GroupLayout g = requireGroupLayout();
137 long offset = 0;
138 MemoryLayout elem = null;
139 for (int i = 0; i < g.memberLayouts().size(); i++) {
140 MemoryLayout l = g.memberLayouts().get(i);
141 if (l.name().isPresent() &&
142 l.name().get().equals(name)) {
143 elem = l;
144 break;
145 } else if (g instanceof StructLayout) {
146 offset += l.byteSize();
147 }
148 }
149 if (elem == null) {
150 throw badLayoutPath(
151 String.format("cannot resolve '%s' in layout %s", name, breadcrumbs()));
152 }
153 return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
154 }
155
156 public LayoutPath groupElement(long index) {
157 GroupLayout g = requireGroupLayout();
158 long elemSize = g.memberLayouts().size();
159 long offset = 0;
160 MemoryLayout elem = null;
161 for (int i = 0; i <= index; i++) {
162 if (i == elemSize) {
163 throw badLayoutPath(
164 String.format("cannot resolve element %d in layout: %s", index, breadcrumbs()));
165 }
166 elem = g.memberLayouts().get(i);
167 if (g instanceof StructLayout && i < index) {
168 offset += elem.byteSize();
169 }
170 }
171 return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
172 }
173
174 public LayoutPath derefElement() {
175 if (!(layout instanceof AddressLayout addressLayout) ||
176 addressLayout.targetLayout().isEmpty()) {
177 throw badLayoutPath(
178 String.format("Cannot dereference layout: %s", breadcrumbs()));
179 }
180 MemoryLayout derefLayout = addressLayout.targetLayout().get();
181 MethodHandle handle = dereferenceHandle(false).toMethodHandle(VarHandle.AccessMode.GET);
182 handle = MethodHandles.filterReturnValue(handle,
183 MethodHandles.insertArguments(MH_SEGMENT_RESIZE, 1, derefLayout));
184 return derefPath(derefLayout, handle, this);
185 }
186
187 private static MemorySegment resizeSegment(MemorySegment segment, MemoryLayout layout) {
188 return Utils.longToAddress(segment.address(), layout.byteSize(), layout.byteAlignment());
189 }
190
191 // Layout path projections
192
193 public long offset() {
194 return offset;
195 }
196
197 public VarHandle dereferenceHandle() {
198 return dereferenceHandle(true);
199 }
200
201 public VarHandle dereferenceHandle(boolean adapt) {
202 if (!(layout instanceof ValueLayout valueLayout)) {
203 throw new IllegalArgumentException(
204 String.format("Path does not select a value layout: %s", breadcrumbs()));
205 }
206
207 // If we have an enclosing layout, drop the alignment check for the accessed element,
208 // we check the root layout instead
209 ValueLayout accessedLayout = enclosing != null ? valueLayout.withByteAlignment(1) : valueLayout;
210 VarHandle handle = accessedLayout.varHandle();
211 handle = MethodHandles.collectCoordinates(handle, 1, offsetHandle());
212
213 // we only have to check the alignment of the root layout for the first dereference we do,
214 // as each dereference checks the alignment of the target address when constructing its segment
215 // (see Utils::longToAddress)
216 if (derefAdapters.length == 0 && enclosing != null) {
217 // insert align check for the root layout on the initial MS + offset
218 List<Class<?>> coordinateTypes = handle.coordinateTypes();
219 MethodHandle alignCheck = MethodHandles.insertArguments(MH_CHECK_ALIGN, 2, rootLayout());
220 handle = MethodHandles.collectCoordinates(handle, 0, alignCheck);
221 int[] reorder = IntStream.concat(IntStream.of(0, 1), IntStream.range(0, coordinateTypes.size())).toArray();
222 handle = MethodHandles.permuteCoordinates(handle, coordinateTypes, reorder);
223 }
224
225 if (adapt) {
226 if (derefAdapters.length > 0) {
227 // plug up the base offset if we have at least 1 enclosing dereference
228 handle = MethodHandles.insertCoordinates(handle, 1, 0);
229 }
230 for (int i = derefAdapters.length; i > 0; i--) {
231 MethodHandle adapter = derefAdapters[i - 1];
232 // the first/outermost adapter will have a base offset coordinate, the rest are constant 0
233 if (i > 1) {
234 // plug in a constant 0 base offset for all but the outermost access in a deref chain
235 adapter = MethodHandles.insertArguments(adapter, 1, 0);
236 }
237 handle = MethodHandles.collectCoordinates(handle, 0, adapter);
238 }
239 }
240 return handle;
241 }
242
243 @ForceInline
244 private static long addScaledOffset(long base, long index, long stride, long bound) {
245 Objects.checkIndex(index, bound);
246 return base + (stride * index);
247 }
248
249 public MethodHandle offsetHandle() {
250 MethodHandle mh = MethodHandles.insertArguments(MH_ADD, 0, offset);
251 for (int i = strides.length - 1; i >= 0; i--) {
252 MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, strides[i], bounds[i]);
253 // (J, ...) -> J to (J, J, ...) -> J
254 // i.e. new coord is prefixed. Last coord will correspond to innermost layout
255 mh = MethodHandles.collectArguments(mh, 0, collector);
256 }
257
258 return mh;
259 }
260
261 private MemoryLayout rootLayout() {
262 return enclosing != null ? enclosing.rootLayout() : this.layout;
263 }
264
265 public MethodHandle sliceHandle() {
266 MethodHandle sliceHandle;
267 if (enclosing != null) {
268 // drop the alignment check for the accessed element, we check the root layout instead
269 sliceHandle = MH_SLICE; // (MS, long, long) -> MS
270 sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout.byteSize()); // (MS, long) -> MS
271 } else {
272 sliceHandle = MH_SLICE_LAYOUT; // (MS, long, MemoryLayout) -> MS
273 sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout); // (MS, long) -> MS
274 }
275 sliceHandle = MethodHandles.collectArguments(sliceHandle, 1, offsetHandle()); // (MS, long, ...) -> MS
276
277 if (enclosing != null) {
278 // insert align check for the root layout on the initial MS + offset
279 MethodType oldType = sliceHandle.type();
280 MethodHandle alignCheck = MethodHandles.insertArguments(MH_CHECK_ALIGN, 2, rootLayout());
281 sliceHandle = MethodHandles.collectArguments(sliceHandle, 0, alignCheck); // (MS, long, MS, long) -> MS
282 int[] reorder = IntStream.concat(IntStream.of(0, 1), IntStream.range(0, oldType.parameterCount())).toArray();
283 sliceHandle = MethodHandles.permuteArguments(sliceHandle, oldType, reorder); // (MS, long, ...) -> MS
284 }
285
286 return sliceHandle;
287 }
288
289 private static void checkAlign(MemorySegment segment, long offset, MemoryLayout constraint) {
290 if (!((AbstractMemorySegmentImpl) segment).isAlignedForElement(offset, constraint)) {
291 throw new IllegalArgumentException(String.format(
292 "Target offset %d is incompatible with alignment constraint %d (of %s) for segment %s"
293 , offset, constraint.byteAlignment(), constraint, segment));
294 }
295 }
296
297 public MemoryLayout layout() {
298 return layout;
299 }
300
301 // Layout path construction
302
303 public static LayoutPath rootPath(MemoryLayout layout) {
304 return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, EMPTY_DEREF_HANDLES, null);
305 }
306
307 private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath encl) {
308 return new LayoutPath(layout, offset, strides, bounds, derefAdapters, encl);
309 }
310
311 private static LayoutPath derefPath(MemoryLayout layout, MethodHandle handle, LayoutPath encl) {
312 MethodHandle[] handles = Arrays.copyOf(encl.derefAdapters, encl.derefAdapters.length + 1);
313 handles[encl.derefAdapters.length] = handle;
314 return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, handles, null);
315 }
316
317 // Helper methods
318
319 private SequenceLayout requireSequenceLayout() {
320 return requireLayoutType(SequenceLayout.class, "sequence");
321 }
322
323 private GroupLayout requireGroupLayout() {
324 return requireLayoutType(GroupLayout.class, "group");
325 }
326
327 private <T extends MemoryLayout> T requireLayoutType(Class<T> layoutClass, String name) {
328 if (!layoutClass.isAssignableFrom(layout.getClass())) {
329 throw badLayoutPath(
330 String.format("attempting to select a %s element from a non-%s layout: %s",
331 name, name, breadcrumbs()));
332 }
333 return layoutClass.cast(layout);
334 }
335
336 private void checkSequenceBounds(SequenceLayout seq, long index) {
337 if (index >= seq.elementCount()) {
338 throw badLayoutPath(String.format("sequence index out of bounds; index: %d, elementCount is %d for layout %s",
339 index, seq.elementCount(), breadcrumbs()));
340 }
341 }
342
343 private static IllegalArgumentException badLayoutPath(String cause) {
344 return new IllegalArgumentException("Bad layout path: " + cause);
345 }
346
347 private long[] addStride(long stride) {
348 long[] newStrides = Arrays.copyOf(strides, strides.length + 1);
349 newStrides[strides.length] = stride;
350 return newStrides;
351 }
352
353 private long[] addBound(long maxIndex) {
354 long[] newBounds = Arrays.copyOf(bounds, bounds.length + 1);
355 newBounds[bounds.length] = maxIndex;
356 return newBounds;
357 }
358
359 private String breadcrumbs() {
360 return Stream.iterate(this, Objects::nonNull, lp -> lp.enclosing)
361 .map(LayoutPath::layout)
362 .map(Object::toString)
363 .collect(joining(", selected from: "));
364 }
365
366 /**
367 * This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
368 * is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
369 */
370 public static final class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
371
372 public enum PathKind {
373 SEQUENCE_ELEMENT("unbound sequence element"),
374 SEQUENCE_ELEMENT_INDEX("bound sequence element"),
375 SEQUENCE_RANGE("sequence range"),
376 GROUP_ELEMENT("group element"),
377 DEREF_ELEMENT("dereference element");
378
379 final String description;
380
381 PathKind(String description) {
382 this.description = description;
383 }
384
385 public String description() {
|