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