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