1 /*
   2  * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @test
  28  * @modules java.base/sun.nio.ch
  29  *          jdk.incubator.foreign/jdk.internal.foreign
  30  * @run testng TestByteBuffer
  31  */
  32 
  33 
  34 import jdk.incubator.foreign.MappedMemorySegment;
  35 import jdk.incubator.foreign.MemoryLayouts;
  36 import jdk.incubator.foreign.MemoryLayout;
  37 import jdk.incubator.foreign.MemoryAddress;
  38 import jdk.incubator.foreign.MemorySegment;
  39 import jdk.incubator.foreign.MemoryLayout.PathElement;
  40 import jdk.incubator.foreign.SequenceLayout;
  41 
  42 import java.io.File;
  43 import java.io.IOException;
  44 import java.lang.invoke.MethodHandle;
  45 import java.lang.invoke.MethodHandles;
  46 import java.lang.invoke.VarHandle;
  47 import java.lang.ref.WeakReference;
  48 import java.lang.reflect.InvocationTargetException;
  49 import java.lang.reflect.Method;
  50 import java.lang.reflect.Modifier;
  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.HashMap;
  66 import java.util.Map;
  67 import java.util.function.BiConsumer;
  68 import java.util.function.BiFunction;
  69 import java.util.function.Consumer;
  70 import java.util.function.Function;
  71 import java.util.function.Predicate;
  72 import java.util.stream.Stream;
  73 
  74 import jdk.internal.foreign.HeapMemorySegmentImpl;
  75 import jdk.internal.foreign.MappedMemorySegmentImpl;
  76 import jdk.internal.foreign.MemoryAddressImpl;
  77 import jdk.internal.foreign.NativeMemorySegmentImpl;
  78 import org.testng.SkipException;
  79 import org.testng.annotations.*;
  80 import sun.nio.ch.DirectBuffer;
  81 
  82 import static org.testng.Assert.*;
  83 
  84 public class TestByteBuffer {
  85 
  86     static Path tempPath;
  87 
  88     static {
  89         try {
  90             File file = File.createTempFile("buffer", "txt");
  91             file.deleteOnExit();
  92             tempPath = file.toPath();
  93             Files.write(file.toPath(), new byte[256], StandardOpenOption.WRITE);
  94 
  95         } catch (IOException ex) {
  96             throw new ExceptionInInitializerError(ex);
  97         }
  98     }
  99 
 100     static SequenceLayout tuples = MemoryLayout.ofSequence(500,
 101             MemoryLayout.ofStruct(
 102                     MemoryLayouts.BITS_32_BE.withName("index"),
 103                     MemoryLayouts.BITS_32_BE.withName("value")
 104             ));
 105 
 106     static SequenceLayout bytes = MemoryLayout.ofSequence(100,
 107             MemoryLayouts.BITS_8_BE
 108     );
 109 
 110     static SequenceLayout chars = MemoryLayout.ofSequence(100,
 111             MemoryLayouts.BITS_16_BE
 112     );
 113 
 114     static SequenceLayout shorts = MemoryLayout.ofSequence(100,
 115             MemoryLayouts.BITS_16_BE
 116     );
 117 
 118     static SequenceLayout ints = MemoryLayout.ofSequence(100,
 119             MemoryLayouts.BITS_32_BE
 120     );
 121 
 122     static SequenceLayout floats = MemoryLayout.ofSequence(100,
 123             MemoryLayouts.BITS_32_BE
 124     );
 125 
 126     static SequenceLayout longs = MemoryLayout.ofSequence(100,
 127             MemoryLayouts.BITS_64_BE
 128     );
 129 
 130     static SequenceLayout doubles = MemoryLayout.ofSequence(100,
 131             MemoryLayouts.BITS_64_BE
 132     );
 133 
 134     static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index"));
 135     static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
 136 
 137     static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement());
 138     static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement());
 139     static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement());
 140     static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement());
 141     static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement());
 142     static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement());
 143     static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement());
 144 
 145 
 146     static void initTuples(MemoryAddress base) {
 147         for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
 148             indexHandle.set(base, i, (int)i);
 149             valueHandle.set(base, i, (float)(i / 500f));
 150         }
 151     }
 152 
 153     static void checkTuples(MemoryAddress base, ByteBuffer bb) {
 154         for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
 155             assertEquals(bb.getInt(), (int)indexHandle.get(base, i));
 156             assertEquals(bb.getFloat(), (float)valueHandle.get(base, i));
 157         }
 158     }
 159 
 160     static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
 161         for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
 162             handleSetter.accept(base, i);
 163         }
 164     }
 165 
 166     static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
 167                                               Function<ByteBuffer, Z> bufFactory,
 168                                               BiFunction<MemoryAddress, Long, Object> handleExtractor,
 169                                               Function<Z, Object> bufferExtractor) {
 170         long nelems = layout.elementCount().getAsLong();
 171         long elemSize = layout.elementLayout().byteSize();
 172         for (long i = 0 ; i < nelems ; i++) {
 173             long limit = nelems - i;
 174             MemorySegment resizedSegment = base.segment().asSlice(i * elemSize, limit * elemSize);
 175             ByteBuffer bb = resizedSegment.asByteBuffer();
 176             Z z = bufFactory.apply(bb);
 177             for (long j = i ; j < limit ; j++) {
 178                 Object handleValue = handleExtractor.apply(resizedSegment.baseAddress(), j - i);
 179                 Object bufferValue = bufferExtractor.apply(z);
 180                 if (handleValue instanceof Number) {
 181                     assertEquals(((Number)handleValue).longValue(), j);
 182                     assertEquals(((Number)bufferValue).longValue(), j);
 183                 } else {
 184                     assertEquals((long)(char)handleValue, j);
 185                     assertEquals((long)(char)bufferValue, j);
 186                 }
 187             }
 188         }
 189     }
 190 
 191     @Test
 192     public void testOffheap() {
 193         try (MemorySegment segment = MemorySegment.allocateNative(tuples)) {
 194             MemoryAddress base = segment.baseAddress();
 195             initTuples(base);
 196 
 197             ByteBuffer bb = segment.asByteBuffer();
 198             checkTuples(base, bb);
 199         }
 200     }
 201 
 202     @Test
 203     public void testHeap() {
 204         byte[] arr = new byte[(int) tuples.byteSize()];
 205         MemorySegment region = MemorySegment.ofArray(arr);
 206         MemoryAddress base = region.baseAddress();
 207         initTuples(base);
 208 
 209         ByteBuffer bb = region.asByteBuffer();
 210         checkTuples(base, bb);
 211     }
 212 
 213     @Test
 214     public void testChannel() throws Throwable {
 215         File f = new File("test.out");
 216         assertTrue(f.createNewFile());
 217         f.deleteOnExit();
 218 
 219         //write to channel
 220         try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
 221             withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
 222                 MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
 223                 MemoryAddress base = segment.baseAddress();
 224                 initTuples(base);
 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                 MemoryAddress base = segment.baseAddress();
 234                 checkTuples(base, mbb);
 235             });
 236         }
 237     }
 238 
 239     @Test
 240     public void testMappedSegment() throws Throwable {
 241         File f = new File("test2.out");
 242         f.createNewFile();
 243         f.deleteOnExit();
 244 
 245         //write to channel
 246         try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
 247             MemoryAddress base = segment.baseAddress();
 248             initTuples(base);
 249             segment.force();
 250         }
 251 
 252         //read from channel
 253         try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
 254             MemoryAddress base = segment.baseAddress();
 255             checkTuples(base, segment.asByteBuffer());
 256         }
 257     }
 258 
 259     static void withMappedBuffer(FileChannel channel, FileChannel.MapMode mode, long pos, long size, Consumer<MappedByteBuffer> action) throws Throwable {
 260         MappedByteBuffer mbb = channel.map(mode, pos, size);
 261         var ref = new WeakReference<>(mbb);
 262         action.accept(mbb);
 263         mbb = null;
 264         //wait for it to be GCed
 265         System.gc();
 266         while (ref.get() != null) {
 267             Thread.sleep(20);
 268         }
 269     }
 270 
 271     static void checkByteArrayAlignment(MemoryLayout layout) {
 272         if (layout.bitSize() > 32
 273                 && System.getProperty("sun.arch.data.model").equals("32")) {
 274             throw new SkipException("avoid unaligned access on 32-bit system");
 275         }
 276     }
 277 
 278     @Test(dataProvider = "bufferOps")
 279     public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
 280         Buffer bb;
 281         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 282             MemoryAddress base = segment.baseAddress();
 283             bb = bufferFactory.apply(segment.asByteBuffer());
 284         }
 285         //outside of scope!!
 286         for (Map.Entry<Method, Object[]> e : members.entrySet()) {
 287             if (!e.getKey().getName().contains("get") &&
 288                             !e.getKey().getName().contains("put")) {
 289                 //skip
 290                 return;
 291             }
 292             try {
 293                 e.getKey().invoke(bb, e.getValue());
 294                 assertTrue(false);
 295             } catch (InvocationTargetException ex) {
 296                 Throwable cause = ex.getCause();
 297                 if (cause instanceof IllegalStateException) {
 298                     //all get/set buffer operation should fail because of the scope check
 299                     assertTrue(ex.getCause().getMessage().contains("not alive"));
 300                 } else {
 301                     //all other exceptions were unexpected - fail
 302                     assertTrue(false);
 303                 }
 304             } catch (Throwable ex) {
 305                 //unexpected exception - fail
 306                 assertTrue(false);
 307             }
 308         }
 309     }
 310 
 311     @Test(dataProvider = "bufferHandleOps")
 312     public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
 313         ByteBuffer bb;
 314         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 315             bb = segment.asByteBuffer();
 316             for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
 317                 MethodHandle handle = e.getKey().bindTo(bufferHandle)
 318                         .asSpreader(Object[].class, e.getValue().length);
 319                 try {
 320                     handle.invoke(e.getValue());
 321                 } catch (UnsupportedOperationException ex) {
 322                     //skip
 323                 } catch (Throwable ex) {
 324                     //should not fail - segment is alive!
 325                     fail();
 326                 }
 327             }
 328         }
 329         for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
 330             try {
 331                 MethodHandle handle = e.getKey().bindTo(bufferHandle)
 332                         .asSpreader(Object[].class, e.getValue().length);
 333                 handle.invoke(e.getValue());
 334                 fail();
 335             } catch (IllegalStateException ex) {
 336                 assertTrue(ex.getMessage().contains("not alive"));
 337             } catch (UnsupportedOperationException ex) {
 338                 //skip
 339             } catch (Throwable ex) {
 340                 fail();
 341             }
 342         }
 343     }
 344 
 345     @Test(dataProvider = "bufferOps")
 346     public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
 347         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 348             MemoryAddress base = segment.baseAddress();
 349             Buffer bb = bufferFactory.apply(segment.asByteBuffer());
 350             assertTrue(bb.isDirect());
 351             DirectBuffer directBuffer = ((DirectBuffer)bb);
 352             assertEquals(directBuffer.address(), ((MemoryAddressImpl)base).unsafeGetOffset());
 353             assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
 354             assertTrue(directBuffer.cleaner() == null);
 355         }
 356     }
 357 
 358     @Test(dataProvider="resizeOps")
 359     public void testResizeOffheap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 360         try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
 361             MemoryAddress base = segment.baseAddress();
 362             initializer.accept(base);
 363             checker.accept(base);
 364         }
 365     }
 366 
 367     @Test(dataProvider="resizeOps")
 368     public void testResizeHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 369         checkByteArrayAlignment(seq.elementLayout());
 370         int capacity = (int)seq.byteSize();
 371         MemoryAddress base = MemorySegment.ofArray(new byte[capacity]).baseAddress();
 372         initializer.accept(base);
 373         checker.accept(base);
 374     }
 375 
 376     @Test(dataProvider="resizeOps")
 377     public void testResizeBuffer(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 378         checkByteArrayAlignment(seq.elementLayout());
 379         int capacity = (int)seq.byteSize();
 380         MemoryAddress base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity])).baseAddress();
 381         initializer.accept(base);
 382         checker.accept(base);
 383     }
 384 
 385     @Test(dataProvider="resizeOps")
 386     public void testResizeRoundtripHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 387         checkByteArrayAlignment(seq.elementLayout());
 388         int capacity = (int)seq.byteSize();
 389         byte[] arr = new byte[capacity];
 390         MemorySegment segment = MemorySegment.ofArray(arr);
 391         MemoryAddress first = segment.baseAddress();
 392         initializer.accept(first);
 393         MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
 394         checker.accept(second);
 395     }
 396 
 397     @Test(dataProvider="resizeOps")
 398     public void testResizeRoundtripNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 399         try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
 400             MemoryAddress first = segment.baseAddress();
 401             initializer.accept(first);
 402             MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
 403             checker.accept(second);
 404         }
 405     }
 406 
 407     @Test(expectedExceptions = IllegalStateException.class)
 408     public void testBufferOnClosedScope() {
 409         MemorySegment leaked;
 410         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 411             leaked = segment;
 412         }
 413         ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok
 414         byteBuffer.get(); // should throw
 415     }
 416 
 417     @Test(expectedExceptions = { UnsupportedOperationException.class,
 418                                  IllegalArgumentException.class })
 419     public void testTooBigForByteBuffer() {
 420         try (MemorySegment segment = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L)) {
 421             segment.asByteBuffer();
 422         }
 423     }
 424 
 425     @Test(dataProvider="resizeOps")
 426     public void testCopyHeapToNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 427         checkByteArrayAlignment(seq.elementLayout());
 428         int bytes = (int)seq.byteSize();
 429         try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes);
 430              MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
 431             initializer.accept(heapArray.baseAddress());
 432             MemoryAddress.copy(heapArray.baseAddress(), nativeArray.baseAddress(), bytes);
 433             checker.accept(nativeArray.baseAddress());
 434         }
 435     }
 436 
 437     @Test(dataProvider="resizeOps")
 438     public void testCopyNativeToHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 439         checkByteArrayAlignment(seq.elementLayout());
 440         int bytes = (int)seq.byteSize();
 441         try (MemorySegment nativeArray = MemorySegment.allocateNative(seq);
 442              MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
 443             initializer.accept(nativeArray.baseAddress());
 444             MemoryAddress.copy(nativeArray.baseAddress(), heapArray.baseAddress(), bytes);
 445             checker.accept(heapArray.baseAddress());
 446         }
 447     }
 448 
 449     @Test(dataProvider="bufferSources")
 450     public void testBufferToSegment(ByteBuffer bb, Predicate<MemorySegment> segmentChecker) {
 451         MemorySegment segment = MemorySegment.ofByteBuffer(bb);
 452         assertEquals(segment.hasAccessModes(MemorySegment.WRITE), !bb.isReadOnly());
 453         assertTrue(segmentChecker.test(segment));
 454         assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
 455         assertTrue(segmentChecker.test(segment.withAccessModes(MemorySegment.READ)));
 456         assertEquals(bb.capacity(), segment.byteSize());
 457         //another round trip
 458         segment = MemorySegment.ofByteBuffer(segment.asByteBuffer());
 459         assertEquals(segment.hasAccessModes(MemorySegment.WRITE), !bb.isReadOnly());
 460         assertTrue(segmentChecker.test(segment));
 461         assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
 462         assertTrue(segmentChecker.test(segment.withAccessModes(MemorySegment.READ)));
 463         assertEquals(bb.capacity(), segment.byteSize());
 464     }
 465 
 466     @Test
 467     public void testRoundTripAccess() {
 468         try(MemorySegment ms = MemorySegment.allocateNative(4)) {
 469             MemorySegment msNoAccess = ms.withAccessModes(MemorySegment.READ); // READ is required to make BB
 470             MemorySegment msRoundTrip = MemorySegment.ofByteBuffer(msNoAccess.asByteBuffer());
 471             assertEquals(msNoAccess.accessModes(), msRoundTrip.accessModes());
 472         }
 473     }
 474 
 475     @Test(expectedExceptions = IllegalStateException.class)
 476     public void testDeadAccessOnClosedBufferSegment() {
 477         MemorySegment s1 = MemorySegment.allocateNative(MemoryLayouts.JAVA_INT);
 478         MemorySegment s2 = MemorySegment.ofByteBuffer(s1.asByteBuffer());
 479 
 480         s1.close(); // memory freed
 481 
 482         intHandle.set(s2.baseAddress(), 0L, 10); // Dead access!
 483     }
 484 
 485     @DataProvider(name = "bufferOps")
 486     public static Object[][] bufferOps() throws Throwable {
 487         return new Object[][]{
 488                 { (Function<ByteBuffer, Buffer>) bb -> bb, bufferMembers(ByteBuffer.class)},
 489                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asCharBuffer, bufferMembers(CharBuffer.class)},
 490                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asShortBuffer, bufferMembers(ShortBuffer.class)},
 491                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asIntBuffer, bufferMembers(IntBuffer.class)},
 492                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asFloatBuffer, bufferMembers(FloatBuffer.class)},
 493                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asLongBuffer, bufferMembers(LongBuffer.class)},
 494                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asDoubleBuffer, bufferMembers(DoubleBuffer.class)},
 495         };
 496     }
 497 
 498     static Map<Method, Object[]> bufferMembers(Class<?> bufferClass) {
 499         Map<Method, Object[]> members = new HashMap<>();
 500         for (Method m : bufferClass.getMethods()) {
 501             //skip statics and method declared in j.l.Object
 502             if (m.getDeclaringClass().equals(Object.class) ||
 503                     (m.getModifiers() & Modifier.STATIC) != 0) continue;
 504             Object[] args = Stream.of(m.getParameterTypes())
 505                     .map(TestByteBuffer::defaultValue)
 506                     .toArray();
 507             members.put(m, args);
 508         }
 509         return members;
 510     }
 511 
 512     @DataProvider(name = "bufferHandleOps")
 513     public static Object[][] bufferHandleOps() throws Throwable {
 514         return new Object[][]{
 515                 { MethodHandles.byteBufferViewVarHandle(char[].class, ByteOrder.nativeOrder()) },
 516                 { MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()) },
 517                 { MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder()) },
 518                 { MethodHandles.byteBufferViewVarHandle(long[].class, ByteOrder.nativeOrder()) },
 519                 { MethodHandles.byteBufferViewVarHandle(float[].class, ByteOrder.nativeOrder()) },
 520                 { MethodHandles.byteBufferViewVarHandle(double[].class, ByteOrder.nativeOrder()) }
 521         };
 522     }
 523 
 524     static Map<MethodHandle, Object[]> varHandleMembers(ByteBuffer bb, VarHandle handle) {
 525         Map<MethodHandle, Object[]> members = new HashMap<>();
 526         for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
 527             Class<?>[] params = handle.accessModeType(mode).parameterArray();
 528             Object[] args = Stream.concat(Stream.of(bb), Stream.of(params).skip(1)
 529                     .map(TestByteBuffer::defaultValue))
 530                     .toArray();
 531             try {
 532                 members.put(MethodHandles.varHandleInvoker(mode, handle.accessModeType(mode)), args);
 533             } catch (Throwable ex) {
 534                 throw new AssertionError(ex);
 535             }
 536         }
 537         return members;
 538     }
 539 
 540     @DataProvider(name = "resizeOps")
 541     public Object[][] resizeOps() {
 542         Consumer<MemoryAddress> byteInitializer =
 543                 (base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
 544         Consumer<MemoryAddress> charInitializer =
 545                 (base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
 546         Consumer<MemoryAddress> shortInitializer =
 547                 (base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
 548         Consumer<MemoryAddress> intInitializer =
 549                 (base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
 550         Consumer<MemoryAddress> floatInitializer =
 551                 (base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
 552         Consumer<MemoryAddress> longInitializer =
 553                 (base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
 554         Consumer<MemoryAddress> doubleInitializer =
 555                 (base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
 556 
 557         Consumer<MemoryAddress> byteChecker =
 558                 (base) -> checkBytes(base, bytes, Function.identity(), byteHandle::get, ByteBuffer::get);
 559         Consumer<MemoryAddress> charChecker =
 560                 (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, charHandle::get, CharBuffer::get);
 561         Consumer<MemoryAddress> shortChecker =
 562                 (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, shortHandle::get, ShortBuffer::get);
 563         Consumer<MemoryAddress> intChecker =
 564                 (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, intHandle::get, IntBuffer::get);
 565         Consumer<MemoryAddress> floatChecker =
 566                 (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, floatHandle::get, FloatBuffer::get);
 567         Consumer<MemoryAddress> longChecker =
 568                 (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, longHandle::get, LongBuffer::get);
 569         Consumer<MemoryAddress> doubleChecker =
 570                 (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, doubleHandle::get, DoubleBuffer::get);
 571 
 572         return new Object[][]{
 573                 {byteChecker, byteInitializer, bytes},
 574                 {charChecker, charInitializer, chars},
 575                 {shortChecker, shortInitializer, shorts},
 576                 {intChecker, intInitializer, ints},
 577                 {floatChecker, floatInitializer, floats},
 578                 {longChecker, longInitializer, longs},
 579                 {doubleChecker, doubleInitializer, doubles}
 580         };
 581     }
 582 
 583     static Object defaultValue(Class<?> c) {
 584         if (c.isPrimitive()) {
 585             if (c == char.class) {
 586                 return (char)0;
 587             } else if (c == boolean.class) {
 588                 return false;
 589             } else if (c == byte.class) {
 590                 return (byte)0;
 591             } else if (c == short.class) {
 592                 return (short)0;
 593             } else if (c == int.class) {
 594                 return 0;
 595             } else if (c == long.class) {
 596                 return 0L;
 597             } else if (c == float.class) {
 598                 return 0f;
 599             } else if (c == double.class) {
 600                 return 0d;
 601             } else {
 602                 throw new IllegalStateException();
 603             }
 604         } else if (c.isArray()) {
 605             if (c == char[].class) {
 606                 return new char[1];
 607             } else if (c == boolean[].class) {
 608                 return new boolean[1];
 609             } else if (c == byte[].class) {
 610                 return new byte[1];
 611             } else if (c == short[].class) {
 612                 return new short[1];
 613             } else if (c == int[].class) {
 614                 return new int[1];
 615             } else if (c == long[].class) {
 616                 return new long[1];
 617             } else if (c == float[].class) {
 618                 return new float[1];
 619             } else if (c == double[].class) {
 620                 return new double[1];
 621             } else {
 622                 throw new IllegalStateException();
 623             }
 624         } else {
 625             return null;
 626         }
 627     }
 628 
 629     @DataProvider(name = "bufferSources")
 630     public static Object[][] bufferSources() {
 631         Predicate<MemorySegment> heapTest = segment -> segment instanceof HeapMemorySegmentImpl;
 632         Predicate<MemorySegment> nativeTest = segment -> segment instanceof NativeMemorySegmentImpl;
 633         Predicate<MemorySegment> mappedTest = segment -> segment instanceof MappedMemorySegmentImpl;
 634         try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
 635             return new Object[][]{
 636                     { ByteBuffer.wrap(new byte[256]), heapTest },
 637                     { ByteBuffer.allocate(256), heapTest },
 638                     { ByteBuffer.allocateDirect(256), nativeTest },
 639                     { channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256), mappedTest },
 640 
 641                     { ByteBuffer.wrap(new byte[256]).asReadOnlyBuffer(), heapTest },
 642                     { ByteBuffer.allocate(256).asReadOnlyBuffer(), heapTest },
 643                     { ByteBuffer.allocateDirect(256).asReadOnlyBuffer(), nativeTest },
 644                     { channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256).asReadOnlyBuffer(),
 645                             nativeTest /* this seems to be an existing bug in the BB implementation */ }
 646             };
 647         } catch (IOException ex) {
 648             throw new ExceptionInInitializerError(ex);
 649         }
 650     }
 651 }