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