< prev index next >

src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java

Print this page

  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() {
< prev index next >