1 /*
  2  * Copyright (c) 2019, 2021, 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @modules java.base/sun.nio.ch
 27  *          jdk.incubator.foreign/jdk.internal.foreign
 28  * @run testng/othervm --enable-native-access=ALL-UNNAMED TestByteBuffer
 29  */
 30 
 31 import jdk.incubator.foreign.MemoryLayout;
 32 import jdk.incubator.foreign.MemoryAddress;
 33 import jdk.incubator.foreign.MemorySegment;
 34 import jdk.incubator.foreign.MemoryLayout.PathElement;
 35 import jdk.incubator.foreign.ResourceScope;
 36 import jdk.incubator.foreign.SequenceLayout;
 37 
 38 import java.io.File;
 39 import java.io.IOException;
 40 import java.lang.invoke.MethodHandle;
 41 import java.lang.invoke.MethodHandles;
 42 import java.lang.invoke.VarHandle;
 43 import java.lang.ref.Cleaner;
 44 import java.lang.ref.WeakReference;
 45 import java.lang.reflect.InvocationTargetException;
 46 import java.lang.reflect.Method;
 47 import java.lang.reflect.Modifier;
 48 import java.net.URI;
 49 import java.nio.Buffer;
 50 import java.nio.ByteBuffer;
 51 import java.nio.ByteOrder;
 52 import java.nio.CharBuffer;
 53 import java.nio.DoubleBuffer;
 54 import java.nio.FloatBuffer;
 55 import java.nio.IntBuffer;
 56 import java.nio.LongBuffer;
 57 import java.nio.MappedByteBuffer;
 58 import java.nio.ShortBuffer;
 59 import java.nio.channels.FileChannel;
 60 import java.nio.file.Files;
 61 import java.nio.file.Path;
 62 import java.nio.file.StandardOpenOption;
 63 import java.util.ArrayList;
 64 import java.util.Arrays;
 65 import java.util.HashMap;
 66 import java.util.List;
 67 import java.util.Map;
 68 import java.util.function.BiConsumer;
 69 import java.util.function.BiFunction;
 70 import java.util.function.Consumer;
 71 import java.util.function.Function;
 72 import java.util.function.Predicate;
 73 import java.util.function.Supplier;
 74 import java.util.stream.Stream;
 75 
 76 import jdk.internal.foreign.HeapMemorySegmentImpl;
 77 import jdk.internal.foreign.MappedMemorySegmentImpl;
 78 import jdk.internal.foreign.NativeMemorySegmentImpl;
 79 import org.testng.SkipException;
 80 import org.testng.annotations.*;
 81 import sun.nio.ch.DirectBuffer;
 82 
 83 import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
 84 import static jdk.incubator.foreign.ValueLayout.JAVA_CHAR;
 85 import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE;
 86 import static jdk.incubator.foreign.ValueLayout.JAVA_FLOAT;
 87 import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
 88 import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
 89 import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
 90 import static org.testng.Assert.*;
 91 
 92 public class TestByteBuffer {
 93 
 94     static Path tempPath;
 95 
 96     static {
 97         try {
 98             File file = File.createTempFile("buffer", "txt");
 99             file.deleteOnExit();
100             tempPath = file.toPath();
101             Files.write(file.toPath(), new byte[256], StandardOpenOption.WRITE);
102 
103         } catch (IOException ex) {
104             throw new ExceptionInInitializerError(ex);
105         }
106     }
107 
108     static SequenceLayout tuples = MemoryLayout.sequenceLayout(500,
109             MemoryLayout.structLayout(
110                     JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withName("index"),
111                     JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN).withName("value")
112             ));
113 
114     static SequenceLayout bytes = MemoryLayout.sequenceLayout(100,
115             JAVA_BYTE
116     );
117 
118     static SequenceLayout chars = MemoryLayout.sequenceLayout(100,
119             JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN)
120     );
121 
122     static SequenceLayout shorts = MemoryLayout.sequenceLayout(100,
123             JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN)
124     );
125 
126     static SequenceLayout ints = MemoryLayout.sequenceLayout(100,
127             JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN)
128     );
129 
130     static SequenceLayout floats = MemoryLayout.sequenceLayout(100,
131             JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN)
132     );
133 
134     static SequenceLayout longs = MemoryLayout.sequenceLayout(100,
135             JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN)
136     );
137 
138     static SequenceLayout doubles = MemoryLayout.sequenceLayout(100,
139             JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN)
140     );
141 
142     static VarHandle indexHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("index"));
143     static VarHandle valueHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("value"));
144 
145     static void initTuples(MemorySegment base, long count) {
146         for (long i = 0; i < count ; i++) {
147             indexHandle.set(base, i, (int)i);
148             valueHandle.set(base, i, (float)(i / 500f));
149         }
150     }
151 
152     static void checkTuples(MemorySegment base, ByteBuffer bb, long count) {
153         for (long i = 0; i < count ; i++) {
154             int index;
155             float value;
156             assertEquals(index = bb.getInt(), (int)indexHandle.get(base, i));
157             assertEquals(value = bb.getFloat(), (float)valueHandle.get(base, i));
158             assertEquals(value, index / 500f);
159         }
160     }
161 
162     static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {
163         for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
164             handleSetter.accept(base, i);
165         }
166     }
167 
168     static <Z extends Buffer> void checkBytes(MemorySegment base, SequenceLayout layout,
169                                               Function<ByteBuffer, Z> bufFactory,
170                                               BiFunction<MemorySegment, Long, Object> handleExtractor,
171                                               Function<Z, Object> bufferExtractor) {
172         long nelems = layout.elementCount().getAsLong();
173         long elemSize = layout.elementLayout().byteSize();
174         for (long i = 0 ; i < nelems ; i++) {
175             long limit = nelems - i;
176             MemorySegment resizedSegment = base.asSlice(i * elemSize, limit * elemSize);
177             ByteBuffer bb = resizedSegment.asByteBuffer();
178             Z z = bufFactory.apply(bb);
179             for (long j = i ; j < limit ; j++) {
180                 Object handleValue = handleExtractor.apply(resizedSegment, j - i);
181                 Object bufferValue = bufferExtractor.apply(z);
182                 if (handleValue instanceof Number) {
183                     assertEquals(((Number)handleValue).longValue(), j);
184                     assertEquals(((Number)bufferValue).longValue(), j);
185                 } else {
186                     assertEquals((long)(char)handleValue, j);
187                     assertEquals((long)(char)bufferValue, j);
188                 }
189             }
190         }
191     }
192 
193     @Test
194     public void testOffheap() {
195         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
196             MemorySegment segment = MemorySegment.allocateNative(tuples, scope);
197             initTuples(segment, tuples.elementCount().getAsLong());
198 
199             ByteBuffer bb = segment.asByteBuffer();
200             checkTuples(segment, bb, tuples.elementCount().getAsLong());
201         }
202     }
203 
204     @Test
205     public void testHeap() {
206         byte[] arr = new byte[(int) tuples.byteSize()];
207         MemorySegment region = MemorySegment.ofArray(arr);
208         initTuples(region, tuples.elementCount().getAsLong());
209 
210         ByteBuffer bb = region.asByteBuffer();
211         checkTuples(region, bb, tuples.elementCount().getAsLong());
212     }
213 
214     @Test
215     public void testChannel() throws Throwable {
216         File f = new File("test.out");
217         assertTrue(f.createNewFile());
218         f.deleteOnExit();
219 
220         //write to channel
221         try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
222             withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
223                 MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
224                 initTuples(segment, tuples.elementCount().getAsLong());
225                 mbb.force();
226             });
227         }
228 
229         //read from channel
230         try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
231             withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {
232                 MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
233                 checkTuples(segment, mbb, tuples.elementCount().getAsLong());
234             });
235         }
236     }
237 
238     @Test
239     public void testDefaultAccessModesMappedSegment() throws Throwable {
240         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
241             MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_WRITE, scope);
242             assertFalse(segment.isReadOnly());
243         }
244 
245         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
246             MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_ONLY, scope);
247             assertTrue(segment.isReadOnly());
248         }
249     }
250 
251     @Test
252     public void testMappedSegment() throws Throwable {
253         File f = new File("test2.out");
254         f.createNewFile();
255         f.deleteOnExit();
256 
257         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
258             //write to channel
259             MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_WRITE, scope);
260             initTuples(segment, tuples.elementCount().getAsLong());
261             segment.force();
262         }
263 
264         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
265             //read from channel
266             MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY, scope);
267             checkTuples(segment, segment.asByteBuffer(), tuples.elementCount().getAsLong());
268         }
269     }
270 
271     @Test(dataProvider = "mappedOps", expectedExceptions = IllegalStateException.class)
272     public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable {
273         File f = new File("test3.out");
274         f.createNewFile();
275         f.deleteOnExit();
276 
277         MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 8, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());
278         assertTrue(segment.isMapped());
279         segment.scope().close();
280         mappedBufferOp.apply(segment);
281     }
282 
283     @Test
284     public void testMappedSegmentOffset() throws Throwable {
285         File f = new File("test3.out");
286         f.createNewFile();
287         f.deleteOnExit();
288 
289         MemoryLayout tupleLayout = tuples.elementLayout();
290 
291         // write one at a time
292         for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
293             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
294                 //write to channel
295                 MemorySegment segment = MemorySegment.mapFile(f.toPath(), i, tuples.byteSize(), FileChannel.MapMode.READ_WRITE, scope);
296                 initTuples(segment, 1);
297                 segment.force();
298             }
299         }
300 
301         // check one at a time
302         for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
303             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
304                 //read from channel
305                 MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY, scope);
306                 checkTuples(segment, segment.asByteBuffer(), 1);
307             }
308         }
309     }
310 
311     static final long LARGE_SIZE = 3L * 1024L * 1024L * 1024L; // 3GB
312 
313     @Test
314     public void testLargeMappedSegment() throws Throwable {
315         if (System.getProperty("sun.arch.data.model").equals("32")) {
316             throw new SkipException("large mapped files not supported on 32-bit systems");
317         }
318 
319         File f = new File("testLargeMappedSegment.out");
320         f.createNewFile();
321         f.deleteOnExit();
322 
323         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
324             MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, LARGE_SIZE, FileChannel.MapMode.READ_WRITE, scope);
325             segment.isLoaded();
326             segment.load();
327             segment.isLoaded();
328             segment.force();
329             segment.isLoaded();
330             segment.unload();
331             segment.isLoaded();
332         } catch(IOException e) {
333             if (e.getMessage().equals("Function not implemented"))
334                 throw new SkipException(e.getMessage(), e);
335         }
336     }
337 
338     static void withMappedBuffer(FileChannel channel, FileChannel.MapMode mode, long pos, long size, Consumer<MappedByteBuffer> action) throws Throwable {
339         MappedByteBuffer mbb = channel.map(mode, pos, size);
340         var ref = new WeakReference<>(mbb);
341         action.accept(mbb);
342         mbb = null;
343         //wait for it to be GCed
344         System.gc();
345         while (ref.get() != null) {
346             Thread.sleep(20);
347         }
348     }
349 
350     static void checkByteArrayAlignment(MemoryLayout layout) {
351         if (layout.bitSize() > 32
352                 && System.getProperty("sun.arch.data.model").equals("32")) {
353             throw new SkipException("avoid unaligned access on 32-bit system");
354         }
355     }
356 
357     @Test(dataProvider = "bufferOps")
358     public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {
359         Buffer bb;
360         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
361             MemorySegment segment = MemorySegment.allocateNative(bytes, scope);
362             bb = bufferFactory.apply(segment.asByteBuffer());
363         }
364         //outside of scope!!
365         try {
366             method.invoke(bb, args);
367             fail("Exception expected");
368         } catch (InvocationTargetException ex) {
369             Throwable cause = ex.getCause();
370             if (cause instanceof IllegalStateException) {
371                 //all get/set buffer operation should fail because of the scope check
372                 assertTrue(ex.getCause().getMessage().contains("Already closed"));
373             } else {
374                 //all other exceptions were unexpected - fail
375                 fail("Unexpected exception", cause);
376             }
377         } catch (Throwable ex) {
378             //unexpected exception - fail
379             fail("Unexpected exception", ex);
380         }
381     }
382 
383     @Test(dataProvider = "bufferHandleOps")
384     public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
385         ByteBuffer bb;
386         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
387             MemorySegment segment = MemorySegment.allocateNative(bytes, scope);
388             bb = segment.asByteBuffer();
389             for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
390                 MethodHandle handle = e.getKey().bindTo(bufferHandle)
391                         .asSpreader(Object[].class, e.getValue().length);
392                 try {
393                     handle.invoke(e.getValue());
394                 } catch (UnsupportedOperationException ex) {
395                     //skip
396                 } catch (Throwable ex) {
397                     //should not fail - segment is alive!
398                     fail();
399                 }
400             }
401         }
402         for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
403             try {
404                 MethodHandle handle = e.getKey().bindTo(bufferHandle)
405                         .asSpreader(Object[].class, e.getValue().length);
406                 handle.invoke(e.getValue());
407                 fail();
408             } catch (IllegalStateException ex) {
409                 assertTrue(ex.getMessage().contains("Already closed"));
410             } catch (UnsupportedOperationException ex) {
411                 //skip
412             } catch (Throwable ex) {
413                 fail();
414             }
415         }
416     }
417 
418     @Test(dataProvider = "bufferOps")
419     public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {
420         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
421             MemorySegment segment = MemorySegment.allocateNative(bytes, scope);
422             Buffer bb = bufferFactory.apply(segment.asByteBuffer());
423             assertTrue(bb.isDirect());
424             DirectBuffer directBuffer = ((DirectBuffer)bb);
425             assertEquals(directBuffer.address(), segment.address().toRawLongValue());
426             assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
427             assertTrue(directBuffer.cleaner() == null);
428         }
429     }
430 
431     @Test(dataProvider="resizeOps")
432     public void testResizeOffheap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
433         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
434             MemorySegment segment = MemorySegment.allocateNative(seq, scope);
435             initializer.accept(segment);
436             checker.accept(segment);
437         }
438     }
439 
440     @Test(dataProvider="resizeOps")
441     public void testResizeHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
442         checkByteArrayAlignment(seq.elementLayout());
443         int capacity = (int)seq.byteSize();
444         MemorySegment base = MemorySegment.ofArray(new byte[capacity]);
445         initializer.accept(base);
446         checker.accept(base);
447     }
448 
449     @Test(dataProvider="resizeOps")
450     public void testResizeBuffer(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
451         checkByteArrayAlignment(seq.elementLayout());
452         int capacity = (int)seq.byteSize();
453         MemorySegment base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity]));
454         initializer.accept(base);
455         checker.accept(base);
456     }
457 
458     @Test(dataProvider="resizeOps")
459     public void testResizeRoundtripHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
460         checkByteArrayAlignment(seq.elementLayout());
461         int capacity = (int)seq.byteSize();
462         byte[] arr = new byte[capacity];
463         MemorySegment segment = MemorySegment.ofArray(arr);
464         initializer.accept(segment);
465         MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer());
466         checker.accept(second);
467     }
468 
469     @Test(dataProvider="resizeOps")
470     public void testResizeRoundtripNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
471         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
472             MemorySegment segment = MemorySegment.allocateNative(seq, scope);
473             initializer.accept(segment);
474             MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer());
475             checker.accept(second);
476         }
477     }
478 
479     @Test(expectedExceptions = IllegalStateException.class)
480     public void testBufferOnClosedScope() {
481         MemorySegment leaked;
482         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
483             leaked = MemorySegment.allocateNative(bytes, scope);
484         }
485         ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok
486         byteBuffer.get(); // should throw
487     }
488 
489     @Test(expectedExceptions = IllegalStateException.class)
490     public void testTooBigForByteBuffer() {
491         MemorySegment segment = MemorySegment.ofAddressNative(MemoryAddress.NULL, Integer.MAX_VALUE + 10L, ResourceScope.newImplicitScope());
492         segment.asByteBuffer();
493     }
494 
495     @Test(expectedExceptions = IllegalArgumentException.class)
496     public void testBadMapNegativeSize() throws IOException {
497         File f = new File("testNeg1.out");
498         f.createNewFile();
499         f.deleteOnExit();
500         MemorySegment.mapFile(f.toPath(), 0L, -1, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());
501     }
502 
503     @Test(expectedExceptions = IllegalArgumentException.class)
504     public void testBadMapNegativeOffset() throws IOException {
505         File f = new File("testNeg2.out");
506         f.createNewFile();
507         f.deleteOnExit();
508         MemorySegment.mapFile(f.toPath(), -1, 1, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());
509     }
510 
511     @Test
512     public void testMapOffset() throws IOException {
513         File f = new File("testMapOffset.out");
514         f.createNewFile();
515         f.deleteOnExit();
516 
517         int SIZE = Byte.MAX_VALUE;
518 
519         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
520             MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, SIZE, FileChannel.MapMode.READ_WRITE, scope);
521             for (byte offset = 0; offset < SIZE; offset++) {
522                 segment.set(JAVA_BYTE, offset, offset);
523             }
524             segment.force();
525         }
526 
527         for (int offset = 0 ; offset < SIZE ; offset++) {
528             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
529                 MemorySegment segment = MemorySegment.mapFile(f.toPath(), offset, SIZE - offset, FileChannel.MapMode.READ_ONLY, scope);
530                 assertEquals(segment.get(JAVA_BYTE, 0), offset);
531             }
532         }
533     }
534 
535     @Test
536     public void testMapZeroSize() throws IOException {
537         File f = new File("testPos1.out");
538         f.createNewFile();
539         f.deleteOnExit();
540         //RW
541         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
542             MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_WRITE, scope);
543             assertEquals(segment.byteSize(), 0);
544             assertEquals(segment.isMapped(), true);
545             assertFalse(segment.isReadOnly());
546             segment.force();
547             segment.load();
548             segment.isLoaded();
549             segment.unload();
550         }
551         //RO
552         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
553             MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_ONLY, scope);
554             assertEquals(segment.byteSize(), 0);
555             assertEquals(segment.isMapped(), true);
556             assertTrue(segment.isReadOnly());
557             segment.force();
558             segment.load();
559             segment.isLoaded();
560             segment.unload();
561         }
562     }
563 
564     @Test(expectedExceptions = IllegalArgumentException.class)
565     public void testMapCustomPath() throws IOException {
566         Path path = Path.of(URI.create("jrt:/"));
567         MemorySegment.mapFile(path, 0L, 0L, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());
568     }
569 
570     @Test(dataProvider="resizeOps")
571     public void testCopyHeapToNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
572         checkByteArrayAlignment(seq.elementLayout());
573         int bytes = (int)seq.byteSize();
574         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
575             MemorySegment nativeArray = MemorySegment.allocateNative(bytes, 1, scope);
576             MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);
577             initializer.accept(heapArray);
578             nativeArray.copyFrom(heapArray);
579             checker.accept(nativeArray);
580         }
581     }
582 
583     @Test(dataProvider="resizeOps")
584     public void testCopyNativeToHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
585         checkByteArrayAlignment(seq.elementLayout());
586         int bytes = (int)seq.byteSize();
587         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
588             MemorySegment nativeArray = MemorySegment.allocateNative(seq, scope);
589             MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);
590             initializer.accept(nativeArray);
591             heapArray.copyFrom(nativeArray);
592             checker.accept(heapArray);
593         }
594     }
595 
596     @Test
597     public void testDefaultAccessModesOfBuffer() {
598         ByteBuffer rwBuffer = ByteBuffer.wrap(new byte[4]);
599         {
600             MemorySegment segment = MemorySegment.ofByteBuffer(rwBuffer);
601             assertFalse(segment.isReadOnly());
602         }
603 
604         {
605             ByteBuffer roBuffer = rwBuffer.asReadOnlyBuffer();
606             MemorySegment segment = MemorySegment.ofByteBuffer(roBuffer);
607             assertTrue(segment.isReadOnly());
608         }
609     }
610 
611     @Test(dataProvider="bufferSources")
612     public void testBufferToSegment(ByteBuffer bb, Predicate<MemorySegment> segmentChecker) {
613         MemorySegment segment = MemorySegment.ofByteBuffer(bb);
614         assertEquals(segment.isReadOnly(), bb.isReadOnly());
615         assertTrue(segmentChecker.test(segment));
616         assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
617         assertEquals(bb.capacity(), segment.byteSize());
618         //another round trip
619         segment = MemorySegment.ofByteBuffer(segment.asByteBuffer());
620         assertEquals(segment.isReadOnly(), bb.isReadOnly());
621         assertTrue(segmentChecker.test(segment));
622         assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
623         assertEquals(bb.capacity(), segment.byteSize());
624     }
625 
626     @Test(dataProvider="bufferSources")
627     public void bufferProperties(ByteBuffer bb, Predicate<MemorySegment> _unused) {
628         MemorySegment segment = MemorySegment.ofByteBuffer(bb);
629         ByteBuffer buffer = segment.asByteBuffer();
630         assertEquals(buffer.position(), 0);
631         assertEquals(buffer.capacity(), segment.byteSize());
632         assertEquals(buffer.limit(), segment.byteSize());
633     }
634 
635     @Test
636     public void testRoundTripAccess() {
637         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
638             MemorySegment ms = MemorySegment.allocateNative(4, 1, scope);
639             MemorySegment msNoAccess = ms.asReadOnly();
640             MemorySegment msRoundTrip = MemorySegment.ofByteBuffer(msNoAccess.asByteBuffer());
641             assertEquals(msNoAccess.isReadOnly(), msRoundTrip.isReadOnly());
642         }
643     }
644 
645     @Test(expectedExceptions = IllegalStateException.class)
646     public void testDeadAccessOnClosedBufferSegment() {
647         MemorySegment s1 = MemorySegment.allocateNative(JAVA_INT, ResourceScope.newConfinedScope());
648         MemorySegment s2 = MemorySegment.ofByteBuffer(s1.asByteBuffer());
649 
650         // memory freed
651         s1.scope().close();
652 
653         s2.set(JAVA_INT, 0, 10); // Dead access!
654     }
655 
656     @Test(dataProvider = "allScopes")
657     public void testIOOnSegmentBuffer(Supplier<ResourceScope> scopeSupplier) throws IOException {
658         File tmp = File.createTempFile("tmp", "txt");
659         tmp.deleteOnExit();
660         ResourceScope scope;
661         try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE) ;
662              ResourceScope scp = closeableScopeOrNull(scope = scopeSupplier.get())) {
663             MemorySegment segment = MemorySegment.allocateNative(10, 1, scope);
664             for (int i = 0; i < 10; i++) {
665                 segment.set(JAVA_BYTE, i, (byte) i);
666             }
667             ByteBuffer bb = segment.asByteBuffer();
668             assertEquals(channel.write(bb), 10);
669             segment.fill((byte)0x00);
670             assertEquals(bb.clear(), ByteBuffer.wrap(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
671             assertEquals(channel.position(0).read(bb.clear()), 10);
672             assertEquals(bb.flip(), ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
673         }
674     }
675 
676     static final Class<IllegalStateException> ISE = IllegalStateException.class;
677 
678     @Test(dataProvider = "closeableScopes")
679     public void testIOOnClosedSegmentBuffer(Supplier<ResourceScope> scopeSupplier) throws IOException {
680         File tmp = File.createTempFile("tmp", "txt");
681         tmp.deleteOnExit();
682         try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
683             MemorySegment segment = MemorySegment.allocateNative(10, scopeSupplier.get());
684             for (int i = 0; i < 10; i++) {
685                 segment.set(JAVA_BYTE, i, (byte) i);
686             }
687             ByteBuffer bb = segment.asByteBuffer();
688             segment.scope().close();
689             assertThrows(ISE, () -> channel.read(bb));
690             assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}));
691             assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}, 0, 1));
692             assertThrows(ISE, () -> channel.write(bb));
693             assertThrows(ISE, () -> channel.write(new ByteBuffer[] {bb}));
694             assertThrows(ISE, () -> channel.write(new ByteBuffer[] {bb}, 0 ,1));
695         }
696     }
697 
698     @Test
699     public void buffersAndArraysFromSlices() {
700         try (ResourceScope scope = ResourceScope.newSharedScope()) {
701             MemorySegment segment = MemorySegment.allocateNative(16, scope);
702             int newSize = 8;
703             var slice = segment.asSlice(4, newSize);
704 
705             var bytes = slice.toArray(JAVA_BYTE);
706             assertEquals(newSize, bytes.length);
707 
708             var buffer = slice.asByteBuffer();
709             // Fails for heap segments, but passes for native segments:
710             assertEquals(0, buffer.position());
711             assertEquals(newSize, buffer.limit());
712             assertEquals(newSize, buffer.capacity());
713         }
714     }
715 
716     @Test
717     public void viewsFromSharedSegment() {
718         try (ResourceScope scope = ResourceScope.newSharedScope()) {
719             MemorySegment segment = MemorySegment.allocateNative(16, scope);
720             var byteBuffer = segment.asByteBuffer();
721             byteBuffer.asReadOnlyBuffer();
722             byteBuffer.slice(0, 8);
723         }
724     }
725 
726     @DataProvider(name = "segments")
727     public static Object[][] segments() throws Throwable {
728         return new Object[][] {
729                 { (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newImplicitScope()) },
730                 { (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newConfinedScope()) },
731                 { (Supplier<MemorySegment>) () -> MemorySegment.ofArray(new byte[16]) }
732         };
733     }
734 
735     @DataProvider(name = "closeableScopes")
736     public static Object[][] closeableScopes() {
737         return new Object[][] {
738                 { (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope()   },
739                 { (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope() },
740                 { (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope(Cleaner.create())   },
741                 { (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope(Cleaner.create()) },
742                 { (Supplier<ResourceScope>) () -> ResourceScope.newImplicitScope() }
743         };
744     }
745 
746     @DataProvider(name = "allScopes")
747     public static Object[][] allScopes() {
748         return Stream.of(new Object[][] { { (Supplier<ResourceScope>)ResourceScope::globalScope } }, closeableScopes())
749                 .flatMap(Arrays::stream)
750                 .toArray(Object[][]::new);
751     }
752 
753     static ResourceScope closeableScopeOrNull(ResourceScope scope) {
754         if (scope == ResourceScope.globalScope())
755             return null;
756         return scope;
757     }
758 
759     @DataProvider(name = "bufferOps")
760     public static Object[][] bufferOps() throws Throwable {
761         List<Object[]> args = new ArrayList<>();
762         bufferOpsArgs(args, bb -> bb, ByteBuffer.class);
763         bufferOpsArgs(args, ByteBuffer::asCharBuffer, CharBuffer.class);
764         bufferOpsArgs(args, ByteBuffer::asShortBuffer, ShortBuffer.class);
765         bufferOpsArgs(args, ByteBuffer::asIntBuffer, IntBuffer.class);
766         bufferOpsArgs(args, ByteBuffer::asFloatBuffer, FloatBuffer.class);
767         bufferOpsArgs(args, ByteBuffer::asLongBuffer, LongBuffer.class);
768         bufferOpsArgs(args, ByteBuffer::asDoubleBuffer, DoubleBuffer.class);
769         return args.toArray(Object[][]::new);
770     }
771 
772     static void bufferOpsArgs(List<Object[]> argsList, Function<ByteBuffer, Buffer> factory, Class<?> bufferClass) {
773         for (Method m : bufferClass.getMethods()) {
774             //skip statics and method declared in j.l.Object
775             if (m.getDeclaringClass().equals(Object.class)
776                 || ((m.getModifiers() & Modifier.STATIC) != 0)
777                 || (!m.getName().contains("get") && !m.getName().contains("put"))
778                 || m.getParameterCount() > 2) continue;
779             Object[] args = Stream.of(m.getParameterTypes())
780                     .map(TestByteBuffer::defaultValue)
781                     .toArray();
782             argsList.add(new Object[] { factory, m, args });
783         }
784     }
785 
786     @DataProvider(name = "bufferHandleOps")
787     public static Object[][] bufferHandleOps() throws Throwable {
788         return new Object[][]{
789                 { MethodHandles.byteBufferViewVarHandle(char[].class, ByteOrder.nativeOrder()) },
790                 { MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()) },
791                 { MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder()) },
792                 { MethodHandles.byteBufferViewVarHandle(long[].class, ByteOrder.nativeOrder()) },
793                 { MethodHandles.byteBufferViewVarHandle(float[].class, ByteOrder.nativeOrder()) },
794                 { MethodHandles.byteBufferViewVarHandle(double[].class, ByteOrder.nativeOrder()) }
795         };
796     }
797 
798     static Map<MethodHandle, Object[]> varHandleMembers(ByteBuffer bb, VarHandle handle) {
799         Map<MethodHandle, Object[]> members = new HashMap<>();
800         for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
801             Class<?>[] params = handle.accessModeType(mode).parameterArray();
802             Object[] args = Stream.concat(Stream.of(bb), Stream.of(params).skip(1)
803                     .map(TestByteBuffer::defaultValue))
804                     .toArray();
805             try {
806                 members.put(MethodHandles.varHandleInvoker(mode, handle.accessModeType(mode)), args);
807             } catch (Throwable ex) {
808                 throw new AssertionError(ex);
809             }
810         }
811         return members;
812     }
813 
814     @DataProvider(name = "resizeOps")
815     public Object[][] resizeOps() {
816         Consumer<MemorySegment> byteInitializer =
817                 (base) -> initBytes(base, bytes, (addr, pos) -> addr.set(JAVA_BYTE, pos, (byte)(long)pos));
818         Consumer<MemorySegment> charInitializer =
819                 (base) -> initBytes(base, chars, (addr, pos) -> addr.setAtIndex(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), pos, (char)(long)pos));
820         Consumer<MemorySegment> shortInitializer =
821                 (base) -> initBytes(base, shorts, (addr, pos) -> addr.setAtIndex(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), pos, (short)(long)pos));
822         Consumer<MemorySegment> intInitializer =
823                 (base) -> initBytes(base, ints, (addr, pos) -> addr.setAtIndex(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), pos, (int)(long)pos));
824         Consumer<MemorySegment> floatInitializer =
825                 (base) -> initBytes(base, floats, (addr, pos) -> addr.setAtIndex(JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), pos, (float)(long)pos));
826         Consumer<MemorySegment> longInitializer =
827                 (base) -> initBytes(base, longs, (addr, pos) -> addr.setAtIndex(JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), pos, (long)pos));
828         Consumer<MemorySegment> doubleInitializer =
829                 (base) -> initBytes(base, doubles, (addr, pos) -> addr.setAtIndex(JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), pos, (double)(long)pos));
830 
831         Consumer<MemorySegment> byteChecker =
832                 (base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> addr.get(JAVA_BYTE, pos), ByteBuffer::get);
833         Consumer<MemorySegment> charChecker =
834                 (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> addr.getAtIndex(JAVA_CHAR.withOrder(ByteOrder.BIG_ENDIAN), pos), CharBuffer::get);
835         Consumer<MemorySegment> shortChecker =
836                 (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> addr.getAtIndex(JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN), pos), ShortBuffer::get);
837         Consumer<MemorySegment> intChecker =
838                 (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> addr.getAtIndex(JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN), pos), IntBuffer::get);
839         Consumer<MemorySegment> floatChecker =
840                 (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> addr.getAtIndex(JAVA_FLOAT.withOrder(ByteOrder.BIG_ENDIAN), pos), FloatBuffer::get);
841         Consumer<MemorySegment> longChecker =
842                 (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> addr.getAtIndex(JAVA_LONG.withOrder(ByteOrder.BIG_ENDIAN), pos), LongBuffer::get);
843         Consumer<MemorySegment> doubleChecker =
844                 (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> addr.getAtIndex(JAVA_DOUBLE.withOrder(ByteOrder.BIG_ENDIAN), pos), DoubleBuffer::get);
845 
846         return new Object[][]{
847                 {byteChecker, byteInitializer, bytes},
848                 {charChecker, charInitializer, chars},
849                 {shortChecker, shortInitializer, shorts},
850                 {intChecker, intInitializer, ints},
851                 {floatChecker, floatInitializer, floats},
852                 {longChecker, longInitializer, longs},
853                 {doubleChecker, doubleInitializer, doubles}
854         };
855     }
856 
857     static Object defaultValue(Class<?> c) {
858         if (c.isPrimitive()) {
859             if (c == char.class) {
860                 return (char)0;
861             } else if (c == boolean.class) {
862                 return false;
863             } else if (c == byte.class) {
864                 return (byte)0;
865             } else if (c == short.class) {
866                 return (short)0;
867             } else if (c == int.class) {
868                 return 0;
869             } else if (c == long.class) {
870                 return 0L;
871             } else if (c == float.class) {
872                 return 0f;
873             } else if (c == double.class) {
874                 return 0d;
875             } else {
876                 throw new IllegalStateException();
877             }
878         } else if (c.isArray()) {
879             if (c == char[].class) {
880                 return new char[1];
881             } else if (c == boolean[].class) {
882                 return new boolean[1];
883             } else if (c == byte[].class) {
884                 return new byte[1];
885             } else if (c == short[].class) {
886                 return new short[1];
887             } else if (c == int[].class) {
888                 return new int[1];
889             } else if (c == long[].class) {
890                 return new long[1];
891             } else if (c == float[].class) {
892                 return new float[1];
893             } else if (c == double[].class) {
894                 return new double[1];
895             } else {
896                 throw new IllegalStateException();
897             }
898         } else if (c == String.class) {
899             return "asdf";
900         } else if (c == ByteBuffer.class) {
901             return ByteBuffer.wrap(new byte[1]);
902         } else if (c == CharBuffer.class) {
903             return CharBuffer.wrap(new char[1]);
904         } else if (c == ShortBuffer.class) {
905             return ShortBuffer.wrap(new short[1]);
906         } else if (c == IntBuffer.class) {
907             return IntBuffer.wrap(new int[1]);
908         } else if (c == FloatBuffer.class) {
909             return FloatBuffer.wrap(new float[1]);
910         } else if (c == LongBuffer.class) {
911             return LongBuffer.wrap(new long[1]);
912         } else if (c == DoubleBuffer.class) {
913             return DoubleBuffer.wrap(new double[1]);
914         } else {
915             return null;
916         }
917     }
918 
919     @DataProvider(name = "bufferSources")
920     public static Object[][] bufferSources() {
921         Predicate<MemorySegment> heapTest = segment -> segment instanceof HeapMemorySegmentImpl;
922         Predicate<MemorySegment> nativeTest = segment -> segment instanceof NativeMemorySegmentImpl;
923         Predicate<MemorySegment> mappedTest = segment -> segment instanceof MappedMemorySegmentImpl;
924         try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
925             return new Object[][]{
926                     { ByteBuffer.wrap(new byte[256]), heapTest },
927                     { ByteBuffer.allocate(256), heapTest },
928                     { ByteBuffer.allocateDirect(256), nativeTest },
929                     { channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256), mappedTest },
930 
931                     { ByteBuffer.wrap(new byte[256]).asReadOnlyBuffer(), heapTest },
932                     { ByteBuffer.allocate(256).asReadOnlyBuffer(), heapTest },
933                     { ByteBuffer.allocateDirect(256).asReadOnlyBuffer(), nativeTest },
934                     { channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256).asReadOnlyBuffer(),
935                             nativeTest /* this seems to be an existing bug in the BB implementation */ }
936             };
937         } catch (IOException ex) {
938             throw new ExceptionInInitializerError(ex);
939         }
940     }
941 
942     enum MappedSegmentOp {
943         LOAD(MemorySegment::load),
944         UNLOAD(MemorySegment::unload),
945         IS_LOADED(MemorySegment::isLoaded),
946         FORCE(MemorySegment::force),
947         BUFFER_LOAD(m -> ((MappedByteBuffer)m.asByteBuffer()).load()),
948         BUFFER_IS_LOADED(m -> ((MappedByteBuffer)m.asByteBuffer()).isLoaded()),
949         BUFFER_FORCE(m -> ((MappedByteBuffer)m.asByteBuffer()).force());
950 
951 
952         private Consumer<MemorySegment> segmentOp;
953 
954         MappedSegmentOp(Consumer<MemorySegment> segmentOp) {
955             this.segmentOp = segmentOp;
956         }
957 
958         void apply(MemorySegment segment) {
959             segmentOp.accept(segment);
960         }
961     }
962 
963     @DataProvider(name = "mappedOps")
964     public static Object[][] mappedOps() {
965         return Stream.of(MappedSegmentOp.values())
966                 .map(op -> new Object[] { op })
967                 .toArray(Object[][]::new);
968     }
969 }