|
269 | 269 | * access modes. All other access modes will result in {@link UnsupportedOperationException} being thrown. Moreover,
|
270 | 270 | * while supported, access modes {@code get} and {@code set} might lead to word tearing.
|
271 | 271 | *
|
272 |
| - * <h2 id="variable-length">Working with variable-length structs</h2> |
| 272 | + * <h2 id="variable-length">Working with variable-length arrays</h2> |
273 | 273 | *
|
274 |
| - * Memory layouts allow clients to describe the contents of a region of memory whose size is known <em>statically</em>. |
275 |
| - * There are, however, cases, where the size of a region of memory is only known <em>dynamically</em>, as it depends |
276 |
| - * on the value of one or more struct fields. Consider the following struct declaration in C: |
| 274 | + * We have seen how sequence layouts are used to describe the contents of an array whose size is known <em>statically</em>. |
| 275 | + * There are cases, however, where the array size is only known <em>dynamically</em>. We call such arrays <em>variable-length arrays</em>. |
| 276 | + * There are two common kinds of variable-length arrays: |
| 277 | + * <ul> |
| 278 | + * <li>a <em>toplevel</em> variable-length array whose size depends on the value of some unrelated variable, or parameter;</li> |
| 279 | + * <li>an variable-length array <em>nested</em> in a struct, whose size depends on the value of some other field in the enclosing struct.</li> |
| 280 | + * </ul> |
| 281 | + * While variable-length arrays cannot be modelled directly using sequence layouts, clients can still enjoy structured |
| 282 | + * access to elements of variable-length arrays using var handles as demonstrated in the following sections. |
| 283 | + * |
| 284 | + * <h3 id="variable-length-toplevel">Toplevel variable-length arrays</h3> |
| 285 | + * |
| 286 | + * Consider the following struct declaration in C: |
| 287 | + * |
| 288 | + * {@snippet lang=c : |
| 289 | + * typedef struct { |
| 290 | + * int x; |
| 291 | + * int y; |
| 292 | + * } Point; |
| 293 | + * } |
| 294 | + * |
| 295 | + * In the above code, a point is modelled as two coordinates ({@code x} and {@code y} respectively). Now consider |
| 296 | + * the following snippet of C code: |
| 297 | + * |
| 298 | + * {@snippet lang=c : |
| 299 | + * int size = ... |
| 300 | + * Point *points = (Point*)malloc(sizeof(Point) * size); |
| 301 | + * for (int i = 0 ; i < size ; i++) { |
| 302 | + * ... points[i].x ... |
| 303 | + * } |
| 304 | + * } |
| 305 | + * |
| 306 | + * Here, we allocate an array of point ({@code points}). Crucially, the size of the array is dynamically bound to the value |
| 307 | + * of the {@code size} variable. Inside the loop, the {@code x} coordinate of all the points in the array is accessed. |
| 308 | + * <p> |
| 309 | + * To model this code in Java, let's start by defining a layout for the {@code Point} struct, as follows: |
| 310 | + * |
| 311 | + * {@snippet lang=java : |
| 312 | + * StructLayout POINT = MemoryLayout.structLayout( |
| 313 | + * ValueLayout.JAVA_INT.withName("x"), |
| 314 | + * ValueLayout.JAVA_INT.withName("y") |
| 315 | + * ); |
| 316 | + * } |
| 317 | + * |
| 318 | + * Since we know we need to create and access an array of points, it would be tempting to create a sequence layout modelling |
| 319 | + * the variable-length array, and then derive the necessary access var handles from the sequence layout. But this approach |
| 320 | + * is problematic, as the size of the variable-length array is not known. Instead, a var handle that provides structured |
| 321 | + * access to the elements of a variable-length array can be obtained directly from the layout describing the array elements |
| 322 | + * (e.g. the point layout), as demonstrated below: |
| 323 | + * |
| 324 | + * {@snippet lang=java : |
| 325 | + * VarHandle POINT_ARR_X = POINT.arrayElementVarHandle(PathElement.groupElement("x")); |
| 326 | + * |
| 327 | + * int size = ... |
| 328 | + * MemorySegment points = ... |
| 329 | + * for (int i = 0 ; i < size ; i++) { |
| 330 | + * ... POINT_ARR_X.get(segment, 0L, (long)i) ... |
| 331 | + * } |
| 332 | + * } |
| 333 | + * |
| 334 | + * Here, the coordinate {@code x} of subsequent point in the array is accessed using the {@code POINT_ARR_X} var |
| 335 | + * handle, which is obtained using the {@link #arrayElementVarHandle(PathElement...)} method. This var handle |
| 336 | + * features two {@code long} coordinates: the first is a base offset (set to {@code 0L}), while the |
| 337 | + * second is a logical index that can be used to stride over all the elements of the point array. |
| 338 | + * <p> |
| 339 | + * The base offset coordinate allows clients to express complex access operations, by injecting additional offset |
| 340 | + * computation into the var handle (we will see an example of that below). In cases where the base offset is constant |
| 341 | + * (as in the previous example) clients can, if desired, drop the base offset parameter and make the access expression |
| 342 | + * simpler. This is achieved using the {@link java.lang.invoke.MethodHandles#insertCoordinates(VarHandle, int, Object...)} |
| 343 | + * var handle adapter. |
| 344 | + * |
| 345 | + * <h3 id="variable-length-nested">Nested variable-length arrays</h3> |
| 346 | + * |
| 347 | + * Consider the following struct declaration in C: |
277 | 348 | *
|
278 | 349 | * {@snippet lang=c :
|
279 | 350 | * typedef struct {
|
280 | 351 | * int size;
|
281 |
| - * struct { |
282 |
| - * int x; |
283 |
| - * int y; |
284 |
| - * } points[]; |
| 352 | + * Point points[]; |
285 | 353 | * } Polygon;
|
286 | 354 | * }
|
287 | 355 | *
|
|
290 | 358 | * the size of the {@code points} array is left <em>unspecified</em> in the C declaration, using a <em>Flexible Array Member</em>
|
291 | 359 | * (a feature standardized in C99).
|
292 | 360 | * <p>
|
293 |
| - * Memory layouts do not support sequence layouts whose size is unknown. As such, it is not possible to model |
294 |
| - * the above struct directly. That said, clients can still enjoy structured access provided by memory layouts, as |
295 |
| - * demonstrated below: |
| 361 | + * Again, clients can perform structured access to elements in the nested variable-length array using the |
| 362 | + * {@link #arrayElementVarHandle(PathElement...)} method, as demonstrated below: |
296 | 363 | *
|
297 | 364 | * {@snippet lang=java :
|
298 |
| - * StructLayout POINT = MemoryLayout.structLayout( |
299 |
| - * ValueLayout.JAVA_INT.withName("x"), |
300 |
| - * ValueLayout.JAVA_INT.withName("y") |
301 |
| - * ); |
302 |
| - * |
303 | 365 | * StructLayout POLYGON = MemoryLayout.structLayout(
|
304 | 366 | * ValueLayout.JAVA_INT.withName("size"),
|
305 | 367 | * MemoryLayout.sequenceLayout(0, POINT).withName("points")
|
306 | 368 | * );
|
307 | 369 | *
|
308 | 370 | * VarHandle POLYGON_SIZE = POLYGON.varHandle(0, PathElement.groupElement("size"));
|
309 |
| - * VarHandle POINT_X = POINT.varHandle(PathElement.groupElement("x")); |
310 | 371 | * long POINTS_OFFSET = POLYGON.byteOffset(PathElement.groupElement("points"));
|
311 | 372 | * }
|
312 | 373 | *
|
313 |
| - * Note how we have split the polygon struct in two. The {@code POLYGON} layout contains a sequence layout |
314 |
| - * of size <em>zero</em>. The element layout of the sequence layout is the {@code POINT} layout, which defines |
315 |
| - * the {@code x} and {@code y} coordinates, accordingly. The first layout is used to obtain a var handle |
316 |
| - * that provides access to the polygon size; the second layout is used to obtain a var handle that provides |
317 |
| - * access to the {@code x} coordinate of a point struct. Finally, an offset to the start of the variable-length |
318 |
| - * {@code points} array is also obtained. |
| 374 | + * The {@code POLYGON} layout contains a sequence layout of size <em>zero</em>. The element layout of the sequence layout |
| 375 | + * is the {@code POINT} layout, shown previously. The polygon layout is used to obtain a var handle |
| 376 | + * that provides access to the polygon size, as well as an offset ({@code POINTS_OFFSET}) to the start of the |
| 377 | + * variable-length {@code points} array. |
319 | 378 | * <p>
|
320 | 379 | * The {@code x} coordinates of all the points in a polygon can then be accessed as follows:
|
321 | 380 | * {@snippet lang=java :
|
322 | 381 | * MemorySegment polygon = ...
|
323 | 382 | * int size = POLYGON_SIZE.get(polygon, 0L);
|
324 | 383 | * for (int i = 0 ; i < size ; i++) {
|
325 |
| - * int x = POINT_X.get(polygon, POINT.scaleOffset(POINTS_OFFSET, i)); |
| 384 | + * ... POINT_ARR_X.get(polygon, POINTS_OFFSET, (long)i) ... |
326 | 385 | * }
|
327 | 386 | * }
|
328 | 387 | * Here, we first obtain the polygon size, using the {@code POLYGON_SIZE} var handle. Then, in a loop, we read
|
329 |
| - * the {@code x} coordinates of all the points in the polygon. This is done by providing a custom base offset to |
330 |
| - * the {@code POINT_X} var handle. The custom offset is computed as {@code POINTS_OFFSET + (i * POINT.byteSize())}, where |
331 |
| - * {@code i} is the loop induction variable. |
| 388 | + * the {@code x} coordinates of all the points in the polygon. This is done by providing a custom offset |
| 389 | + * (namely, {@code POINTS_OFFSET}) to the offset coordinate of the {@code POINT_ARR_X} var handle. As before, |
| 390 | + * the loop induction variable {@code i} is passed as the index of the {@code POINT_ARR_X} var handle, |
| 391 | + * to stride over all the elements of the variable-length array. |
332 | 392 | *
|
333 | 393 | * @implSpec
|
334 | 394 | * Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
|
@@ -544,6 +604,49 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
544 | 604 | */
|
545 | 605 | VarHandle varHandle(PathElement... elements);
|
546 | 606 |
|
| 607 | + /** |
| 608 | + * Creates a var handle that accesses adjacent elements in a memory segment at offsets selected by the given layout path, |
| 609 | + * where the accessed elements have this layout, and where the initial layout in the path is this layout. |
| 610 | + * <p> |
| 611 | + * The returned var handle has the following characteristics: |
| 612 | + * <ul> |
| 613 | + * <li>its type is derived from the {@linkplain ValueLayout#carrier() carrier} of the |
| 614 | + * selected value layout;</li> |
| 615 | + * <li>it has a leading parameter of type {@code MemorySegment} representing the accessed segment</li> |
| 616 | + * <li>a following {@code long} parameter, corresponding to the base offset, denoted as {@code B};</li> |
| 617 | + * <li>a following {@code long} parameter, corresponding to the array index, denoted as {@code I0}. The array |
| 618 | + * index is used to scale the accessed offset by this layout size;</li> |
| 619 | + * <li>it has zero or more trailing access coordinates of type {@code long}, one for each |
| 620 | + * <a href=#open-path-elements>open path element</a> in the provided layout path, denoted as |
| 621 | + * {@code I1, I2, ... In}, respectively. The order of these access coordinates corresponds to the order |
| 622 | + * in which the open path elements occur in the provided layout path. |
| 623 | + * </ul> |
| 624 | + * <p> |
| 625 | + * If the provided layout path {@code P} contains no dereference elements, then the offset {@code O} of the access |
| 626 | + * operation is computed as follows: |
| 627 | + * |
| 628 | + * {@snippet lang = "java": |
| 629 | + * O = this.offsetHandle(P).invokeExact(this.scale(B, I0), I1, I2, ... In); |
| 630 | + * } |
| 631 | + * <p> |
| 632 | + * More formally, this method can be obtained from the {@link #varHandle(PathElement...)}, as follows: |
| 633 | + * {@snippet lang = "java": |
| 634 | + * MethodHandles.collectCoordinates(varHandle(elements), 1, scaleHandle()) |
| 635 | + * } |
| 636 | + * |
| 637 | + * @apiNote |
| 638 | + * As the leading index coordinate {@code I0} is not bound by any sequence layout, it can assume <em>any</em> non-negative |
| 639 | + * value - provided that the resulting offset computation does not overflow, or that the computed offset does not fall |
| 640 | + * outside the spatial bound of the accessed memory segment. As such, the var handles returned from this method can |
| 641 | + * be especially useful when accessing <a href="#variable-length">variable-length arrays</a>. |
| 642 | + * |
| 643 | + * @param elements the layout path elements. |
| 644 | + * @return a var handle that accesses adjacent elements in a memory segment at offsets selected by the given layout path. |
| 645 | + * @throws IllegalArgumentException if the layout path is not <a href="#well-formedness">well-formed</a> for this layout. |
| 646 | + * @throws IllegalArgumentException if the layout selected by the provided path is not a {@linkplain ValueLayout value layout}. |
| 647 | + */ |
| 648 | + VarHandle arrayElementVarHandle(PathElement... elements); |
| 649 | + |
547 | 650 | /**
|
548 | 651 | * Creates a method handle which, given a memory segment, returns a {@linkplain MemorySegment#asSlice(long,long) slice}
|
549 | 652 | * corresponding to the layout selected by the given layout path, where the initial layout in the path is this layout.
|
|
0 commit comments