1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
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 package hat.ifacemapper;
26
27 import hat.buffer.Buffer;
28
29 import java.lang.foreign.Arena;
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.util.Objects;
39 import java.util.Optional;
40 import java.util.OptionalLong;
41 import java.util.function.Function;
42 import java.util.stream.Stream;
43
44 /**
45 * A segment mapper can project memory segment onto and from class instances.
46 * <p>
47 * More specifically, a segment mapper can project a backing
48 * {@linkplain MemorySegment MemorySegment} into new {@link Record} instances or new
49 * instances that implements an interface by means of matching the names of the record
50 * components or interface methods with the names of member layouts in a group layout.
51 * A segment mapper can also be used in the other direction, where records and interface
52 * implementing instances can be used to update a target memory segment. By using any of
53 * the {@linkplain #map(Class, Function) map} operations, segment mappers can be
54 * used to map between memory segments and additional Java types other than record and
55 * interfaces (such as JavaBeans).
56 *
57 * <p>
58 * In short, a segment mapper finds, for each record component or interface method,
59 * a corresponding member layout with the same name in the group layout. There are some
60 * restrictions on the record component type and the corresponding member layout type
61 * (e.g. a record component of type {@code int} can only be matched with a member layout
62 * having a carrier type of {@code int.class} (such as {@link ValueLayout#JAVA_INT})).
63 * <p>
64 * Using the member layouts (e.g. observing offsets and
65 * {@link java.nio.ByteOrder byte ordering}), a number of extraction methods are then
66 * identified for all the record components or interface methods and these are stored
67 * internally in the segment mapper.
68 *
69 * <h2 id="mapping-kinds">Mapping kinds</h2>
70 * <p>
71 * Segment mappers can be of two fundamental kinds;
72 * <ul>
73 * <li>Record</li>
74 * <li>Interface</li>
75 * </ul>
76 * <p>
77 * The characteristics of the mapper kinds are summarized in the following table:
78 *
79 * <blockquote><table class="plain">
80 * <caption style="display:none">Mapper characteristics</caption>
81 * <thead>
82 * <tr>
83 * <th scope="col">Mapper kind</th>
84 * <th scope="col">Temporal mode</th>
85 * <th scope="col">Get operations</th>
86 * <th scope="col">Set operations</th>
87 * <th scope="col">Segment access</th>
88 * </tr>
89 * </thead>
90 * <tbody>
91 * <tr><th scope="row" style="font-weight:normal">Record</th>
92 * <td style="text-align:center;">Eager</td>
93 * <td style="text-align:center;">Extract all component values from the source segment, build the record</td>
94 * <td style="text-align:center;">Write all component values to the target segment</td>
95 * <td style="text-align:center;">N/A</td></tr>
96 * <tr><th scope="row" style="font-weight:normal">Interface</th>
97 * <td style="text-align:center;">Lazy</td>
98 * <td style="text-align:center;">Wrap the source segment into a new interface instance</td>
99 * <td style="text-align:center;">Copy the relevant values from the initial source segment into the target segment</td>
100 * <td style="text-align:center;">via <code>SegmentMapper::segment</code></td></tr>
101 * </tbody>
102 * </table></blockquote>
103 *
104 * <h2 id="mapping-records">Mapping Records</h2>
105 * <p>
106 * The example below shows how to extract an instance of a public
107 * <em>{@code Point} record class</em> from a {@link MemorySegment} and vice versa:
108 * {@snippet lang = java:
109 *
110 * static final GroupLayout POINT = MemoryLayout.structLayout(JAVA_INT.withName("x"), JAVA_INT.withName("y"));
111 * public record Point(int x, int y){}
112 * //...
113 * MemorySegment segment = MemorySegment.ofArray(new int[]{3, 4, 0, 0});
114 *
115 * // Obtain a SegmentMapper for the Point record type
116 * SegmentMapper<Point> recordMapper = SegmentMapper.ofRecord(Point.class, POINT);
117 *
118 * // Extracts a new Point record from the provided MemorySegment
119 * Point point = recordMapper.get(segment); // Point[x=3, y=4]
120 *
121 * // Writes the Point record to another MemorySegment
122 * MemorySegment otherSegment = Arena.ofAuto().allocate(MemoryLayout.sequenceLayout(2, POINT));
123 * recordMapper.setAtIndex(otherSegment, 1, point); // segment: 0, 0, 3, 4
124 *}
125 * <p>
126 * Boxing, widening, narrowing and general type conversion must be explicitly handled by
127 * user code. In the following example, the above {@code Point} (using primitive
128 * {@code int x} and {@code int y} coordinates) are explicitly mapped to a narrowed
129 * point type (instead using primitive {@code byte x} and {@code byte y} coordinates):
130 * <p>
131 * {@snippet lang = java:
132 * public record NarrowedPoint(byte x, byte y) {
133 *
134 * static NarrowedPoint fromPoint(Point p) {
135 * return new NarrowedPoint((byte) p.x, (byte) p.y);
136 * }
137 *
138 * static Point toPoint(NarrowedPoint p) {
139 * return new Point(p.x, p.y);
140 * }
141 *
142 * }
143 *
144 * SegmentMapper<NarrowedPoint> narrowedPointMapper =
145 * SegmentMapper.ofRecord(Point.class, POINT) // SegmentMapper<Point>
146 * .map(NarrowedPoint.class, NarrowedPoint::fromPoint, NarrowedPoint::toPoint); // SegmentMapper<NarrowedPoint>
147 *
148 * // Extracts a new NarrowedPoint from the provided MemorySegment
149 * NarrowedPoint narrowedPoint = narrowedPointMapper.get(segment); // NarrowedPoint[x=3, y=4]
150 *}
151 *
152 * <h2 id="mapping-interfaces">Mapping Interfaces</h2>
153 * <p>
154 * Here is another example showing how to extract an instance of a public
155 * <em>interface with an external segment</em>:
156 * {@snippet lang = java:
157 *
158 * static final GroupLayout POINT = MemoryLayout.structLayout(JAVA_INT.withName("x"), JAVA_INT.withName("y"));
159 *
160 * public interface Point {
161 * int x();
162 * void x(int x);
163 * int y();
164 * void y(int x);
165 * }
166 *
167 * //...
168 *
169 * MemorySegment segment = MemorySegment.ofArray(new int[]{3, 4, 0, 0});
170 *
171 * SegmentMapper<Point> mapper = SegmentMapper.of(MethodHandles.lookup(), Point.class, POINT);
172 *
173 * // Creates a new Point interface instance with an external segment
174 * Point point = mapper.get(segment); // Point[x=3, y=4]
175 * point.x(6); // Point[x=6, y=4]
176 * point.y(8); // Point[x=6, y=8]
177 *
178 * MemorySegment otherSegment = Arena.ofAuto().allocate(MemoryLayout.sequenceLayout(2, POINT)); // otherSegment: 0, 0, 0, 0
179 * mapper.setAtIndex(otherSegment, 1, point); // segment: 0, 0, 6, 8
180 *}
181 * }
182 * <p>
183 * Boxing, widening, narrowing and general type conversion must be explicitly handled
184 * by user code. In the following example, the above {@code PointAccessor} interface
185 * (using primitive {@code int x} and {@code int y} coordinates) are explicitly mapped to
186 * a narrowed point type (instead using primitive {@code byte x} and
187 * {@code byte y} coordinates):
188 * <p>
189 * {@snippet lang = java:
190 * interface NarrowedPointAccessor {
191 * byte x();
192 * void x(byte x);
193 * byte y();
194 * void y(byte y);
195 *
196 * static NarrowedPointAccessor fromPointAccessor(PointAccessor pa) {
197 * return new NarrowedPointAccessor() {
198 * @Override public byte x() { return (byte)pa.x(); }
199 * @Override public void x(byte x) { pa.x(x); }
200 * @Override public byte y() { return (byte) pa.y();}
201 * @Override public void y(byte y) { pa.y(y); }
202 * };
203 * }
204 *
205 * }
206 *
207 * SegmentMapper<NarrowedPointAccessor> narrowedPointMapper =
208 * // SegmentMapper<PointAccessor>
209 * SegmentMapper.ofInterface(MethodHandles.lookup(), PointAccessor.class, POINT)
210 * // SegmentMapper<NarrowedPointAccessor>
211 * .map(NarrowedPointAccessor.class, NarrowedPointAccessor::fromPointAccessor);
212 *
213 * MemorySegment segment = MemorySegment.ofArray(new int[]{3, 4});
214 *
215 * // Creates a new NarrowedPointAccessor from the provided MemorySegment
216 * NarrowedPointAccessor narrowedPointAccessor = narrowedPointMapper.get(segment); // NarrowedPointAccessor[x=3, y=4]
217 *
218 * MemorySegment otherSegment = Arena.ofAuto().allocate(MemoryLayout.sequenceLayout(2, POINT));
219 * narrowedPointMapper.setAtIndex(otherSegment, 1, narrowedPointAccessor); // otherSegment = 0, 0, 3, 4
220 *}
221 *
222 * <h2 id="segment-exposure">Backing segment exposure</h2>
223 * <p>
224 * Implementations of interfaces that are obtained via segment mappers can be made to
225 * reveal the underlying memory segment and memory segment offset. This is useful when
226 * modelling structs that are passed and/or received by native calls:
227 * <p>
228 * {@snippet lang = java:
229 * static final GroupLayout POINT = MemoryLayout.structLayout(JAVA_INT.withName("x"), JAVA_INT.withName("y"));
230 *
231 * public interface PointAccessor {
232 * int x();
233 * void x(int x);
234 * int y();
235 * void y(int x);
236 * }
237 *
238 * static double nativeDistance(MemorySegment pointStruct) {
239 * // Calls a native method
240 * // ...
241 * }
242 *
243 * public static void main(String[] args) {
244 *
245 * SegmentMapper<PointAccessor> mapper =
246 * SegmentMapper.of(MethodHandles.lookup(), PointAccessor.class, POINT);
247 *
248 * try (Arena arena = Arena.ofConfined()){
249 * // Creates an interface mapper backed by an internal segment
250 * PointAccessor point = mapper.get(arena);
251 * point.x(3);
252 * point.y(4);
253 *
254 * // Pass the backing internal segment to a native method
255 * double distance = nativeDistance(mapper.segment(point).orElseThrow()); // 5
256 * }
257 *
258 * }
259 *}
260 *
261 * <h2 id="formal-mapping">Formal mapping description</h2>
262 * <p>
263 * Components and layouts are matched with respect to their name and the exact return type and/or
264 * the exact parameter types. No widening or narrowing is employed.
265 *
266 * <h2 id="restrictions">Restrictions</h2>
267 * <p>
268 * Generic interfaces need to have their generic type parameters (if any)
269 * know at compile time. This applies to all extended interfaces recursively.
270 * <p>
271 * Interfaces and records must not implement (directly and/or via inheritance) more than
272 * one abstract method with the same name and erased parameter types. Hence, covariant
273 * overriding is not supported.
274 *
275 * @param <T> the type this mapper converts MemorySegments from and to.
276 * @implSpec Implementations of this interface are immutable, thread-safe and
277 * <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
278 * @since 23
279 */
280
281 // Todo: Map components to MemorySegment (escape hatch)
282 // Todo: How do we handle "extra" setters for interfaces? They should not append
283
284 // Cerializer
285 // Todo: Check all exceptions in JavaDocs: See TestScopedOperations
286 // Todo: Consider generating a graphics rendering.
287 // Todo: Add in doc that getting via an AddressValue will return a MS managed by Arena.global()
288 // Todo: Provide safe sharing across threads (e.g. implement a special Interface with piggybacking/volatile access)
289 // Todo: Prevent several variants in a record from being mapped to a union (otherwise, which will "win" when writing?)
290 // Todo: There seams to be a problem with the ByteOrder in the mapper. See TestJepExamplesUnions
291 // Todo: Let SegmentMapper::getHandle and ::setHandle return the sharp types (e.g. Point) see MethodHandles::exactInvoker
292
293 // Done: The generated interface classes should be @ValueBased
294 // Done: Python "Pandas" (tables), Tabular access from array, Joins etc. <- TEST
295 // -> See TestDataProcessingRecord and TestDataProcessingInterface
296 // No: ~map() can be dropped in favour of "manual mapping"~
297 // Done: Interfaces with internal segments should be directly available via separate factory methods
298 // -> Fixed via SegmentMapper::create
299 // Done: Discuss if an exception is thrown in one of the sub-setters... This means partial update of the MS
300 // This can be fixed using double-buffering. Maybe provide a scratch segment somehow that tracks where writes
301 // has been made (via a separate class BufferedMapper?)
302 // -> Fixed via TestInterfaceMapper::doubleBuffered
303 public interface SegmentMapper<T> {
304
305 /**
306 * {@return the type that this mapper is mapping to and from}
307 */
308 Class<T> type();
309
310 /**
311 * {@return the original {@link GroupLayout } that this mapper is using to map
312 * record components or interface methods}
313 * <p>
314 * Composed segment mappers (obtained via either the {@link SegmentMapper#map(Class, Function)}
315 * or the {@link SegmentMapper#map(Class, Function)} will still return the
316 * group layout from the <em>original</em> SegmentMapper.
317 */
318 GroupLayout layout();
319
320 BoundSchema<?> boundSchema();
321 // Convenience methods
322
323 /**
324 * {@return a new instance of type T projected at an internal {@code segment} at
325 * offset zero created by means of invoking the provided {@code arena}}
326 * <p>
327 * Calling this method is equivalent to the following code:
328 * {@snippet lang = java:
329 * get(arena.allocate(layout()));
330 * }
331 *
332 * @param arena from which to {@linkplain Arena#allocate(MemoryLayout) allocate} an
333 * internal memory segment.
334 * @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope}
335 * associated with the provided segment is not
336 * {@linkplain MemorySegment.Scope#isAlive() alive}
337 * @throws WrongThreadException if this method is called from a thread {@code T},
338 * such that {@code isAccessibleBy(T) == false}
339 * @throws IllegalArgumentException if the access operation is
340 * <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraint</a>
341 * of the {@link #layout()}
342 * @throws IndexOutOfBoundsException if
343 * {@code layout().byteSize() > segment.byteSize()}
344 */
345
346 default T allocate(Arena arena, BoundSchema<?> boundSchema) {
347 if (boundSchema == null) {
348 throw new IllegalStateException("No bound Schema provided");
349 }
350 //System.out.println("Alloc 16 byte aligned layout + 16 bytes padded to next 16 bytes "+byteSize+"=>"+extendedByteSizePaddedTo16Bytes);
351 var segment = arena.allocate(BufferState.getLayoutSizeAfterPadding(layout()) + BufferState.byteSize(), BufferState.alignment);
352 new BufferState(segment, BufferState.getLayoutSizeAfterPadding(layout()))
353 .setMagic()
354 .setPtr(segment)
355 .setLength(layout().byteSize())
356 .setState(BufferState.NEW_STATE);
357 // .assignBits(BufferState.BIT_HOST_NEW| BufferState.BIT_HOST_DIRTY);
358 T returnValue= get(segment, layout(), boundSchema);
359 return returnValue;
360 }
361
362 /**
363 * {@return a new instance of type T projected at the provided
364 * external {@code segment} at offset zero}
365 * <p>
366 * Calling this method is equivalent to the following code:
367 * {@snippet lang = java:
368 * get(segment, 0L);
369 *}
370 *
371 * @param segment the external segment to be projected to the new instance
372 * @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope}
373 * associated with the provided segment is not
374 * {@linkplain MemorySegment.Scope#isAlive() alive}
375 * @throws WrongThreadException if this method is called from a thread {@code T},
376 * such that {@code isAccessibleBy(T) == false}
377 * @throws IllegalArgumentException if the access operation is
378 * <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraint</a>
379 * of the {@link #layout()}
380 * @throws IndexOutOfBoundsException if
381 * {@code layout().byteSize() > segment.byteSize()}
382 */
383 default T get(MemorySegment segment) {
384 return get(segment, 0L);
385 }
386
387 default T get(MemorySegment segment, GroupLayout groupLayout, BoundSchema<?> boundSchema) {
388 return get(segment, groupLayout, boundSchema, 0L);
389 }
390
391
392 /**
393 * {@return a new sequential {@code Stream} of elements of type T}
394 * <p>
395 * Calling this method is equivalent to the following code:
396 * {@snippet lang = java:
397 * segment.elements(layout())
398 * .map(this::get);
399 *}
400 *
401 * @param segment to carve out instances from
402 * @throws IllegalArgumentException if {@code layout().byteSize() == 0}.
403 * @throws IllegalArgumentException if {@code segment.byteSize() % layout().byteSize() != 0}.
404 * @throws IllegalArgumentException if {@code layout().byteSize() % layout().byteAlignment() != 0}.
405 * @throws IllegalArgumentException if this segment is
406 * <a href="MemorySegment.html#segment-alignment">incompatible with the
407 * alignment constraint</a> in the layout of this segment mapper.
408 */
409 default Stream<T> stream(MemorySegment segment) {
410 return segment.elements(layout())
411 .map(this::get);
412 }
413
414
415 /**
416 * {@return a new instance of type T projected from at provided
417 * external {@code segment} at the provided {@code offset}}
418 *
419 * @param segment the external segment to be projected at the new instance
420 * @param offset from where in the segment to project the new instance
421 * @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope}
422 * associated with the provided segment is not
423 * {@linkplain MemorySegment.Scope#isAlive() alive}
424 * @throws WrongThreadException if this method is called from a thread {@code T},
425 * such that {@code isAccessibleBy(T) == false}
426 * @throws IllegalArgumentException if the access operation is
427 * <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraint</a>
428 * of the {@link #layout()}
429 * @throws IndexOutOfBoundsException if
430 * {@code offset > segment.byteSize() - layout().byteSize()}
431 */
432 @SuppressWarnings("unchecked")
433 default T get(MemorySegment segment, long offset) {
434 try {
435 return (T) getHandle()
436 .invokeExact(segment, offset);
437 } catch (NullPointerException |
438 IndexOutOfBoundsException |
439 WrongThreadException |
440 IllegalStateException |
441 IllegalArgumentException rethrow) {
442 throw rethrow;
443 } catch (Throwable e) {
444 throw new RuntimeException("Unable to invoke getHandle() with " +
445 "segment=" + segment +
446 ", offset=" + offset, e);
447 }
448 }
449
450 @SuppressWarnings("unchecked")
451 default T get(MemorySegment segment, GroupLayout layout, BoundSchema<?> boundSchema, long offset) {
452 try {
453 return (T) getHandle()
454 .invokeExact(segment, layout, boundSchema, offset);
455 } catch (NullPointerException |
456 IndexOutOfBoundsException |
457 WrongThreadException |
458 IllegalStateException |
459 IllegalArgumentException rethrow) {
460 throw rethrow;
461 } catch (Throwable e) {
462 throw new RuntimeException("Unable to invoke getHandle() with " +
463 "segment=" + segment +
464 ", offset=" + offset, e);
465 }
466 }
467
468 /**
469 * Writes the provided instance {@code t} of type T into the provided {@code segment}
470 * at offset zero.
471 * <p>
472 * Calling this method is equivalent to the following code:
473 * {@snippet lang = java:
474 * set(segment, 0L, t);
475 *}
476 *
477 * @param segment in which to write the provided {@code t}
478 * @param t instance to write into the provided segment
479 * @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope}
480 * associated with this segment is not
481 * {@linkplain MemorySegment.Scope#isAlive() alive}
482 * @throws WrongThreadException if this method is called from a thread {@code T},
483 * such that {@code isAccessibleBy(T) == false}
484 * @throws IllegalArgumentException if the access operation is
485 * <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraint</a>
486 * of the {@link #layout()}
487 * @throws IndexOutOfBoundsException if {@code layout().byteSize() > segment.byteSize()}
488 * @throws UnsupportedOperationException if this segment is
489 * {@linkplain MemorySegment#isReadOnly() read-only}
490 * @throws UnsupportedOperationException if {@code value} is not a
491 * {@linkplain MemorySegment#isNative() native} segment
492 * @throws IllegalArgumentException if an array length does not correspond to the
493 * {@linkplain SequenceLayout#elementCount() element count} of a sequence layout
494 * @throws NullPointerException if a required parameter is {@code null}
495 */
496 default void set(MemorySegment segment, T t) {
497 set(segment, 0L, t);
498 }
499
500 /**
501 * Writes the provided {@code t} instance of type T into the provided {@code segment}
502 * at the provided {@code index} scaled by the {@code layout().byteSize()}}.
503 * <p>
504 * Calling this method is equivalent to the following code:
505 * {@snippet lang = java:
506 * set(segment, layout().byteSize() * index, t);
507 *}
508 *
509 * @param segment in which to write the provided {@code t}
510 * @param index a logical index, the offset in bytes (relative to the provided
511 * segment address) at which the access operation will occur can be
512 * expressed as {@code (index * layout().byteSize())}
513 * @param t instance to write into the provided segment
514 * @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope}
515 * associated with this segment is not
516 * {@linkplain MemorySegment.Scope#isAlive() alive}
517 * @throws WrongThreadException if this method is called from a thread {@code T},
518 * such that {@code isAccessibleBy(T) == false}
519 * @throws IllegalArgumentException if the access operation is
520 * <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraint</a>
521 * of the {@link #layout()}
522 * @throws IndexOutOfBoundsException if {@code offset > segment.byteSize() - layout.byteSize()}
523 * @throws UnsupportedOperationException if this segment is
524 * {@linkplain MemorySegment#isReadOnly() read-only}
525 * @throws UnsupportedOperationException if {@code value} is not a
526 * {@linkplain MemorySegment#isNative() native} segment
527 * @throws IllegalArgumentException if an array length does not correspond to the
528 * {@linkplain SequenceLayout#elementCount() element count} of a sequence layout
529 * @throws NullPointerException if a required parameter is {@code null}
530 */
531 // default void setAtIndex(MemorySegment segment, long index, T t) {
532 // set(segment, layout().byteSize() * index, t);
533 // }
534
535 /**
536 * Writes the provided instance {@code t} of type T into the provided {@code segment}
537 * at the provided {@code offset}.
538 *
539 * @param segment in which to write the provided {@code t}
540 * @param offset offset in bytes (relative to the provided segment address) at which
541 * this access operation will occur
542 * @param t instance to write into the provided segment
543 * @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope}
544 * associated with this segment is not
545 * {@linkplain MemorySegment.Scope#isAlive() alive}
546 * @throws WrongThreadException if this method is called from a thread {@code T},
547 * such that {@code isAccessibleBy(T) == false}
548 * @throws IllegalArgumentException if the access operation is
549 * <a href="MemorySegment.html#segment-alignment">incompatible with the alignment constraint</a>
550 * of the {@link #layout()}
551 * @throws IndexOutOfBoundsException if {@code offset > segment.byteSize() - layout.byteSize()}
552 * @throws UnsupportedOperationException if
553 * this segment is {@linkplain MemorySegment#isReadOnly() read-only}
554 * @throws UnsupportedOperationException if
555 * {@code value} is not a {@linkplain MemorySegment#isNative() native} segment // Todo: only for pointers
556 * @throws IllegalArgumentException if an array length does not correspond to the
557 * {@linkplain SequenceLayout#elementCount() element count} of a sequence layout
558 * @throws NullPointerException if a required parameter is {@code null}
559 */
560 default void set(MemorySegment segment, long offset, T t) {
561 try {
562 setHandle()
563 .invokeExact(segment, offset, (Object) t);
564 } catch (IndexOutOfBoundsException |
565 WrongThreadException |
566 IllegalStateException |
567 IllegalArgumentException |
568 UnsupportedOperationException |
569 NullPointerException rethrow) {
570 throw rethrow;
571 } catch (Throwable e) {
572 throw new RuntimeException("Unable to invoke setHandle() with " +
573 "segment=" + segment +
574 ", offset=" + offset +
575 ", t=" + t, e);
576 }
577 }
578
579 // Basic methods
580
581 /**
582 * {@return a method handle that returns new instances of type T projected at
583 * a provided external {@code MemorySegment} at a provided {@code long} offset}
584 * <p>
585 * The returned method handle has the following characteristics:
586 * <ul>
587 * <li>its return type is {@code T};</li>
588 * <li>it has a leading parameter of type {@code MemorySegment}
589 * corresponding to the memory segment to be accessed</li>
590 * <li>it has a trailing {@code long} parameter, corresponding to
591 * the base offset</li>
592 * </ul>
593 *
594 * @see #get(MemorySegment, long)
595 */
596 MethodHandle getHandle();
597
598 /**
599 * {@return a method handle that writes a provided instance of type T into
600 * a provided {@code MemorySegment} at a provided {@code long} offset}
601 * <p>
602 * The returned method handle has the following characteristics:
603 * <ul>
604 * <li>its return type is void;</li>
605 * <li>it has a leading parameter of type {@code MemorySegment}
606 * corresponding to the memory segment to be accessed</li>
607 * <li>it has a following {@code long} parameter, corresponding to
608 * the base offset</li>
609 * <li>it has a trailing {@code T} parameter, corresponding to
610 * the value to set</li>
611 * </ul>
612 *
613 * @see #set(MemorySegment, long, Object)
614 */
615 MethodHandle setHandle();
616
617 /**
618 * {@return a new segment mapper that would apply the provided {@code toMapper} after
619 * performing get operations on this segment mapper and that would throw an
620 * {@linkplain UnsupportedOperationException} for set operations if this
621 * segment mapper is a record mapper}
622 * <p>
623 * It should be noted that the type R can represent almost any class and is not
624 * restricted to records and interfaces.
625 * <p>
626 * Interface segment mappers returned by this method does not support
627 * {@linkplain #set(MemorySegment, Object) set} operations.
628 *
629 * @param newType the new type the returned mapper shall use
630 * @param toMapper to apply after get operations on this segment mapper
631 * @param <R> the type of the new segment mapper
632 */
633 <R> SegmentMapper<R> map(Class<R> newType,
634 Function<? super T, ? extends R> toMapper);
635
636 /**
637 * {@return the backing segment of the provided {@code source},
638 * or, if no backing segment exists, {@linkplain Optional#empty()}}
639 * <p>
640 * Interfaces obtained from segment mappers have backing segments. Records obtained
641 * from segment mappers do not.
642 *
643 * @param source from which to extract the backing segment
644 */
645 default Optional<MemorySegment> segment(T source) {
646 Objects.requireNonNull(source);
647 return Optional.empty();
648 }
649
650 /**
651 * {@return the offset in the backing segment of the provided {@code source},
652 * or, if no backing segment exists, {@linkplain OptionalLong#empty()}}
653 * <p>
654 * Interfaces obtained from segment mappers have backing segments. Records obtained
655 * from segment mappers do not.
656 *
657 * @param source from which to extract the backing segment
658 */
659 default OptionalLong offset(T source) {
660 Objects.requireNonNull(source);
661 return OptionalLong.empty();
662 }
663
664 /**
665 * {@return a segment mapper that maps {@linkplain MemorySegment memory segments}
666 * to the provided interface {@code type} using the provided {@code layout}
667 * and using the provided {@code lookup}}
668 *
669 * @param lookup to use when performing reflective analysis on the
670 * provided {@code type}
671 * @param type to map memory segment from and to
672 * @param layout to be used when mapping the provided {@code type}
673 * @param <T> the type the returned mapper converts MemorySegments from and to
674 * @throws IllegalArgumentException if the provided {@code type} is not an interface
675 * @throws IllegalArgumentException if the provided {@code type} is a hidden interface
676 * @throws IllegalArgumentException if the provided {@code type} is a sealed interface
677 * @throws IllegalArgumentException if the provided interface {@code type} directly
678 * declares any generic type parameter
679 * @throws IllegalArgumentException if the provided interface {@code type} cannot be
680 * reflectively analysed using the provided {@code lookup}
681 * @throws IllegalArgumentException if the provided interface {@code type} contains
682 * methods for which there are no exact mapping (of names and types) in
683 * the provided {@code layout} or if the provided {@code type} is not public or
684 * if the method is otherwise unable to create a segment mapper as specified above
685 * @implNote The order in which methods appear (e.g. in the {@code toString} method)
686 * is derived from the provided {@code layout}.
687 * @implNote The returned class can be a
688 * <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
689 * class; programmers should treat instances that are
690 * {@linkplain Object#equals(Object) equal} as interchangeable and should
691 * not use instances for synchronization, or unpredictable behavior may
692 * occur. For example, in a future release, synchronization may fail.
693 * @implNote The returned class can be a {@linkplain Class#isHidden() hidden} class.
694 */
695 static <T> SegmentMapper<T> of(MethodHandles.Lookup lookup,
696 Class<T> type,
697 GroupLayout layout) {
698 Objects.requireNonNull(lookup);
699 MapperUtil.requireImplementableInterfaceType(type);
700 Objects.requireNonNull(layout);
701 return SegmentInterfaceMapper.create(lookup, type, layout, null);
702 }
703
704 static <T extends Buffer> SegmentMapper<T> of(MethodHandles.Lookup lookup, Class<T> type, GroupLayout layout, BoundSchema<?> boundSchema) {
705 Objects.requireNonNull(lookup);
706 MapperUtil.requireImplementableInterfaceType(type);
707 Objects.requireNonNull(layout);
708 return SegmentInterfaceMapper.create(lookup, type, layout, boundSchema);
709
710 }
711
712
713 static <T> SegmentMapper<T> of(MethodHandles.Lookup lookup,
714 Class<T> type,
715 MemoryLayout... elements) {
716
717 StructLayout structlayout = MemoryLayout.structLayout(elements).withName(type.getSimpleName());
718 return of(lookup, type, structlayout);
719 }
720
721
722 /**
723 * Interfaces extending this interface will be provided
724 * with additional methods for discovering the backing
725 * memory segment and offset used as the backing storage.
726 */
727 interface Discoverable {
728
729 /**
730 * {@return the backing segment of this instance}
731 */
732 MemorySegment segment();
733
734 /**
735 * {@return the backing segment of this instance}
736 */
737 MemoryLayout layout();
738
739 /**
740 * {@return the offset in the backing segment of this instance}
741 */
742 long offset();
743 }
744
745 }