3131
3232import java .lang .constant .Constable ;
3333import java .lang .constant .DynamicConstantDesc ;
34+ import java .lang .invoke .MethodHandle ;
35+ import java .lang .invoke .MethodHandles ;
3436import java .lang .invoke .VarHandle ;
3537import java .nio .ByteOrder ;
3638import java .util .EnumSet ;
37- import java .util .List ;
3839import java .util .Objects ;
3940import java .util .Optional ;
4041import java .util .OptionalLong ;
173174 * it follows that the memory access var handle {@code valueHandle} will feature an <em>additional</em> {@code long}
174175 * access coordinate.
175176 *
177+ * <p>A layout path with free dimensions can also be used to create an offset-computing method handle, using the
178+ * {@link #bitOffset(PathElement...)} or {@link #byteOffsetHandle(PathElement...)} method. Again, free dimensions are
179+ * translated into {@code long} parameters of the created method handle. The method handle can be used to compute the
180+ * offsets of elements of a sequence at different indices, by supplying these indices when invoking the method handle.
181+ * For instance:
182+ *
183+ * <blockquote><pre>{@code
184+ MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(),
185+ PathElement.groupElement("kind"));
186+ long offset1 = (long) offsetHandle.invokeExact(1L); // 8
187+ long offset2 = (long) offsetHandle.invokeExact(2L); // 16
188+ * }</pre></blockquote>
189+ *
176190 * <h2>Layout attributes</h2>
177191 *
178192 * Layouts can be optionally associated with one or more <em>attributes</em>. A layout attribute forms a <em>name/value</em>
@@ -337,9 +351,6 @@ default long byteAlignment() {
337351 * Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this
338352 * layout.
339353 *
340- * @apiNote if the layout path has one (or more) free dimensions,
341- * the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
342- *
343354 * @param elements the layout path elements.
344355 * @return The offset, in bits, of the layout selected by the layout path in {@code elements}.
345356 * @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
@@ -348,16 +359,46 @@ default long byteAlignment() {
348359 * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
349360 */
350361 default long bitOffset (PathElement ... elements ) {
351- return computePathOp (LayoutPath .rootPath (this , MemoryLayout ::bitSize ), LayoutPath ::offset , EnumSet .of (PathKind .SEQUENCE_ELEMENT , PathKind .SEQUENCE_RANGE ), elements );
362+ return computePathOp (LayoutPath .rootPath (this , MemoryLayout ::bitSize ), LayoutPath ::offset ,
363+ EnumSet .of (PathKind .SEQUENCE_ELEMENT , PathKind .SEQUENCE_RANGE ), elements );
364+ }
365+
366+ /**
367+ * Creates a method handle that can be used to compute the offset, in bits, of the layout selected
368+ * by a given layout path, where the path is considered rooted in this layout.
369+ *
370+ * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
371+ * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()},
372+ * where the order of the parameters corresponds to the order of the path elements.
373+ * The returned method handle can be used to compute a layout offset similar to {@link #bitOffset(PathElement...)},
374+ * but where some sequence indices are specified only when invoking the method handle.
375+ *
376+ * <p>The final offset returned by the method handle is computed as follows:
377+ *
378+ * <blockquote><pre>{@code
379+ offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
380+ * }</pre></blockquote>
381+ *
382+ * where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as {@code long}
383+ * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
384+ * <em>static</em> stride constants which are derived from the layout path.
385+ *
386+ * @param elements the layout path elements.
387+ * @return a method handle that can be used to compute the bit offset of the layout element
388+ * specified by the given layout path elements, when supplied with the missing sequence element indices.
389+ * @throws IllegalArgumentException if the layout path contains one or more path elements that select
390+ * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
391+ * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
392+ */
393+ default MethodHandle bitOffsetHandle (PathElement ... elements ) {
394+ return computePathOp (LayoutPath .rootPath (this , MemoryLayout ::bitSize ), LayoutPath ::offsetHandle ,
395+ EnumSet .of (PathKind .SEQUENCE_RANGE ), elements );
352396 }
353397
354398 /**
355399 * Computes the offset, in bytes, of the layout selected by a given layout path, where the path is considered rooted in this
356400 * layout.
357401 *
358- * @apiNote if the layout path has one (or more) free dimensions,
359- * the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
360- *
361402 * @param elements the layout path elements.
362403 * @return The offset, in bytes, of the layout selected by the layout path in {@code elements}.
363404 * @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
@@ -367,8 +408,44 @@ default long bitOffset(PathElement... elements) {
367408 * or if {@code bitOffset(elements)} is not a multiple of 8.
368409 */
369410 default long byteOffset (PathElement ... elements ) {
370- return Utils .bitsToBytesOrThrow (bitOffset (elements ),
371- () -> new UnsupportedOperationException ("Cannot compute byte offset; bit offset is not a multiple of 8" ));
411+ return Utils .bitsToBytesOrThrow (bitOffset (elements ), Utils .bitsToBytesThrowOffset );
412+ }
413+
414+ /**
415+ * Creates a method handle that can be used to compute the offset, in bytes, of the layout selected
416+ * by a given layout path, where the path is considered rooted in this layout.
417+ *
418+ * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
419+ * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()},
420+ * where the order of the parameters corresponds to the order of the path elements.
421+ * The returned method handle can be used to compute a layout offset similar to {@link #byteOffset(PathElement...)},
422+ * but where some sequence indices are specified only when invoking the method handle.
423+ *
424+ * <p>The final offset returned by the method handle is computed as follows:
425+ *
426+ * <blockquote><pre>{@code
427+ bitOffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
428+ offset = bitOffset / 8
429+ * }</pre></blockquote>
430+ *
431+ * where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as {@code long}
432+ * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
433+ * <em>static</em> stride constants which are derived from the layout path.
434+ *
435+ * <p>The method handle will throw an {@link UnsupportedOperationException} if the computed
436+ * offset in bits is not a multiple of 8.
437+ *
438+ * @param elements the layout path elements.
439+ * @return a method handle that can be used to compute the byte offset of the layout element
440+ * specified by the given layout path elements, when supplied with the missing sequence element indices.
441+ * @throws IllegalArgumentException if the layout path contains one or more path elements that select
442+ * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
443+ * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
444+ */
445+ default MethodHandle byteOffsetHandle (PathElement ... elements ) {
446+ MethodHandle mh = bitOffsetHandle (elements );
447+ mh = MethodHandles .filterReturnValue (mh , Utils .MH_bitsToBytesOrThrowForOffset );
448+ return mh ;
372449 }
373450
374451 /**
0 commit comments