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.MemoryLayouts;
  35 import jdk.incubator.foreign.MemoryLayout;
  36 import jdk.incubator.foreign.MemoryAddress;
  37 import jdk.incubator.foreign.MemorySegment;
  38 import jdk.incubator.foreign.MemoryLayout.PathElement;
  39 import jdk.incubator.foreign.SequenceLayout;
  40 
  41 import java.io.File;
  42 import java.lang.invoke.MethodHandle;
  43 import java.lang.invoke.MethodHandles;
  44 import java.lang.invoke.VarHandle;
  45 import java.lang.ref.WeakReference;
  46 import java.lang.reflect.InvocationTargetException;
  47 import java.lang.reflect.Method;
  48 import java.lang.reflect.Modifier;
  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.InvalidMarkException;
  57 import java.nio.LongBuffer;
  58 import java.nio.MappedByteBuffer;
  59 import java.nio.ShortBuffer;
  60 import java.nio.channels.FileChannel;
  61 import java.nio.file.StandardOpenOption;
  62 import java.util.HashMap;
  63 import java.util.Map;
  64 import java.util.Optional;
  65 import java.util.function.BiConsumer;
  66 import java.util.function.BiFunction;
  67 import java.util.function.Consumer;
  68 import java.util.function.Function;
  69 import java.util.function.Supplier;
  70 import java.util.stream.Stream;
  71 
  72 import jdk.internal.foreign.MemoryAddressImpl;
  73 import org.testng.SkipException;
  74 import org.testng.annotations.*;
  75 import sun.nio.ch.DirectBuffer;
  76 
  77 import static org.testng.Assert.*;
  78 
  79 public class TestByteBuffer {
  80 
  81     static SequenceLayout tuples = MemoryLayout.ofSequence(500,
  82             MemoryLayout.ofStruct(
  83                     MemoryLayouts.BITS_32_BE.withName("index"),
  84                     MemoryLayouts.BITS_32_BE.withName("value")
  85             ));
  86 
  87     static SequenceLayout bytes = MemoryLayout.ofSequence(100,
  88             MemoryLayouts.BITS_8_BE
  89     );
  90 
  91     static SequenceLayout chars = MemoryLayout.ofSequence(100,
  92             MemoryLayouts.BITS_16_BE
  93     );
  94 
  95     static SequenceLayout shorts = MemoryLayout.ofSequence(100,
  96             MemoryLayouts.BITS_16_BE
  97     );
  98 
  99     static SequenceLayout ints = MemoryLayout.ofSequence(100,
 100             MemoryLayouts.BITS_32_BE
 101     );
 102 
 103     static SequenceLayout floats = MemoryLayout.ofSequence(100,
 104             MemoryLayouts.BITS_32_BE
 105     );
 106 
 107     static SequenceLayout longs = MemoryLayout.ofSequence(100,
 108             MemoryLayouts.BITS_64_BE
 109     );
 110 
 111     static SequenceLayout doubles = MemoryLayout.ofSequence(100,
 112             MemoryLayouts.BITS_64_BE
 113     );
 114 
 115     static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index"));
 116     static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
 117 
 118     static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement());
 119     static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement());
 120     static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement());
 121     static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement());
 122     static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement());
 123     static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement());
 124     static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement());
 125 
 126 
 127     static void initTuples(MemoryAddress base) {
 128         for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
 129             indexHandle.set(base, i, (int)i);
 130             valueHandle.set(base, i, (float)(i / 500f));
 131         }
 132     }
 133 
 134     static void checkTuples(MemoryAddress base, ByteBuffer bb) {
 135         for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
 136             assertEquals(bb.getInt(), (int)indexHandle.get(base, i));
 137             assertEquals(bb.getFloat(), (float)valueHandle.get(base, i));
 138         }
 139     }
 140 
 141     static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
 142         for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
 143             handleSetter.accept(base, i);
 144         }
 145     }
 146 
 147     static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
 148                                               Function<ByteBuffer, Z> bufFactory,
 149                                               BiFunction<MemoryAddress, Long, Object> handleExtractor,
 150                                               Function<Z, Object> bufferExtractor) {
 151         long nelems = layout.elementCount().getAsLong();
 152         long elemSize = layout.elementLayout().byteSize();
 153         for (long i = 0 ; i < nelems ; i++) {
 154             long limit = nelems - i;
 155             MemorySegment resizedSegment = base.segment().asSlice(i * elemSize, limit * elemSize);
 156             ByteBuffer bb = resizedSegment.asByteBuffer();
 157             Z z = bufFactory.apply(bb);
 158             for (long j = i ; j < limit ; j++) {
 159                 Object handleValue = handleExtractor.apply(resizedSegment.baseAddress(), j - i);
 160                 Object bufferValue = bufferExtractor.apply(z);
 161                 if (handleValue instanceof Number) {
 162                     assertEquals(((Number)handleValue).longValue(), j);
 163                     assertEquals(((Number)bufferValue).longValue(), j);
 164                 } else {
 165                     assertEquals((long)(char)handleValue, j);
 166                     assertEquals((long)(char)bufferValue, j);
 167                 }
 168             }
 169         }
 170     }
 171 
 172     @Test
 173     public void testOffheap() {
 174         try (MemorySegment segment = MemorySegment.allocateNative(tuples)) {
 175             MemoryAddress base = segment.baseAddress();
 176             initTuples(base);
 177 
 178             ByteBuffer bb = segment.asByteBuffer();
 179             checkTuples(base, bb);
 180         }
 181     }
 182 
 183     @Test
 184     public void testHeap() {
 185         byte[] arr = new byte[(int) tuples.byteSize()];
 186         MemorySegment region = MemorySegment.ofArray(arr);
 187         MemoryAddress base = region.baseAddress();
 188         initTuples(base);
 189 
 190         ByteBuffer bb = region.asByteBuffer();
 191         checkTuples(base, bb);
 192     }
 193 
 194     @Test
 195     public void testChannel() throws Throwable {
 196         File f = new File("test.out");
 197         assertTrue(f.createNewFile());
 198         f.deleteOnExit();
 199 
 200         //write to channel
 201         try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
 202             withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
 203                 MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
 204                 MemoryAddress base = segment.baseAddress();
 205                 initTuples(base);
 206                 mbb.force();
 207             });
 208         }
 209 
 210         //read from channel
 211         try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
 212             withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {
 213                 MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
 214                 MemoryAddress base = segment.baseAddress();
 215                 checkTuples(base, mbb);
 216             });
 217         }
 218     }
 219 
 220     @Test
 221     public void testMappedSegment() throws Throwable {
 222         File f = new File("test2.out");
 223         f.createNewFile();
 224         f.deleteOnExit();
 225 
 226         //write to channel
 227         try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
 228             MemoryAddress base = segment.baseAddress();
 229             initTuples(base);
 230         }
 231 
 232         //read from channel
 233         try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
 234             MemoryAddress base = segment.baseAddress();
 235             checkTuples(base, segment.asByteBuffer());
 236         }
 237     }
 238 
 239     static void withMappedBuffer(FileChannel channel, FileChannel.MapMode mode, long pos, long size, Consumer<MappedByteBuffer> action) throws Throwable {
 240         MappedByteBuffer mbb = channel.map(mode, pos, size);
 241         var ref = new WeakReference<>(mbb);
 242         action.accept(mbb);
 243         mbb = null;
 244         //wait for it to be GCed
 245         System.gc();
 246         while (ref.get() != null) {
 247             Thread.sleep(20);
 248         }
 249     }
 250 
 251     static void checkByteArrayAlignment(MemoryLayout layout) {
 252         if (layout.bitSize() > 32
 253                 && System.getProperty("sun.arch.data.model").equals("32")) {
 254             throw new SkipException("avoid unaligned access on 32-bit system");
 255         }
 256     }
 257 
 258     @Test(dataProvider = "bufferOps")
 259     public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
 260         Buffer bb;
 261         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 262             MemoryAddress base = segment.baseAddress();
 263             bb = bufferFactory.apply(segment.asByteBuffer());
 264         }
 265         //outside of scope!!
 266         for (Map.Entry<Method, Object[]> e : members.entrySet()) {
 267             if (!e.getKey().getName().contains("get") &&
 268                             !e.getKey().getName().contains("put")) {
 269                 //skip
 270                 return;
 271             }
 272             try {
 273                 e.getKey().invoke(bb, e.getValue());
 274                 assertTrue(false);
 275             } catch (InvocationTargetException ex) {
 276                 Throwable cause = ex.getCause();
 277                 if (cause instanceof IllegalStateException) {
 278                     //all get/set buffer operation should fail because of the scope check
 279                     assertTrue(ex.getCause().getMessage().contains("not alive"));
 280                 } else {
 281                     //all other exceptions were unexpected - fail
 282                     assertTrue(false);
 283                 }
 284             } catch (Throwable ex) {
 285                 //unexpected exception - fail
 286                 assertTrue(false);
 287             }
 288         }
 289     }
 290 
 291     @Test(dataProvider = "bufferHandleOps")
 292     public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
 293         ByteBuffer bb;
 294         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 295             bb = segment.asByteBuffer();
 296             for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
 297                 MethodHandle handle = e.getKey().bindTo(bufferHandle)
 298                         .asSpreader(Object[].class, e.getValue().length);
 299                 try {
 300                     handle.invoke(e.getValue());
 301                 } catch (UnsupportedOperationException ex) {
 302                     //skip
 303                 } catch (Throwable ex) {
 304                     //should not fail - segment is alive!
 305                     fail();
 306                 }
 307             }
 308         }
 309         for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
 310             try {
 311                 MethodHandle handle = e.getKey().bindTo(bufferHandle)
 312                         .asSpreader(Object[].class, e.getValue().length);
 313                 handle.invoke(e.getValue());
 314                 fail();
 315             } catch (IllegalStateException ex) {
 316                 assertTrue(ex.getMessage().contains("not alive"));
 317             } catch (UnsupportedOperationException ex) {
 318                 //skip
 319             } catch (Throwable ex) {
 320                 fail();
 321             }
 322         }
 323     }
 324 
 325     @Test(dataProvider = "bufferOps")
 326     public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
 327         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 328             MemoryAddress base = segment.baseAddress();
 329             Buffer bb = bufferFactory.apply(segment.asByteBuffer());
 330             assertTrue(bb.isDirect());
 331             DirectBuffer directBuffer = ((DirectBuffer)bb);
 332             assertEquals(directBuffer.address(), ((MemoryAddressImpl)base).unsafeGetOffset());
 333             assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
 334             assertTrue(directBuffer.cleaner() == null);
 335         }
 336     }
 337 
 338     @Test(dataProvider="resizeOps")
 339     public void testResizeOffheap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 340         try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
 341             MemoryAddress base = segment.baseAddress();
 342             initializer.accept(base);
 343             checker.accept(base);
 344         }
 345     }
 346 
 347     @Test(dataProvider="resizeOps")
 348     public void testResizeHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 349         checkByteArrayAlignment(seq.elementLayout());
 350         int capacity = (int)seq.byteSize();
 351         MemoryAddress base = MemorySegment.ofArray(new byte[capacity]).baseAddress();
 352         initializer.accept(base);
 353         checker.accept(base);
 354     }
 355 
 356     @Test(dataProvider="resizeOps")
 357     public void testResizeBuffer(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 358         checkByteArrayAlignment(seq.elementLayout());
 359         int capacity = (int)seq.byteSize();
 360         MemoryAddress base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity])).baseAddress();
 361         initializer.accept(base);
 362         checker.accept(base);
 363     }
 364 
 365     @Test(dataProvider="resizeOps")
 366     public void testResizeRoundtripHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 367         checkByteArrayAlignment(seq.elementLayout());
 368         int capacity = (int)seq.byteSize();
 369         byte[] arr = new byte[capacity];
 370         MemorySegment segment = MemorySegment.ofArray(arr);
 371         MemoryAddress first = segment.baseAddress();
 372         initializer.accept(first);
 373         MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
 374         checker.accept(second);
 375     }
 376 
 377     @Test(dataProvider="resizeOps")
 378     public void testResizeRoundtripNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 379         try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
 380             MemoryAddress first = segment.baseAddress();
 381             initializer.accept(first);
 382             MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
 383             checker.accept(second);
 384         }
 385     }
 386 
 387     @Test(expectedExceptions = IllegalStateException.class)
 388     public void testBufferOnClosedScope() {
 389         MemorySegment leaked;
 390         try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
 391             leaked = segment;
 392         }
 393         leaked.asByteBuffer();
 394     }
 395 
 396     @Test(expectedExceptions = { UnsupportedOperationException.class,
 397                                  IllegalArgumentException.class })
 398     public void testTooBigForByteBuffer() {
 399         MemorySegment.allocateNative((long) Integer.MAX_VALUE * 2).asByteBuffer();
 400     }
 401 
 402     @Test(dataProvider="resizeOps")
 403     public void testCopyHeapToNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 404         checkByteArrayAlignment(seq.elementLayout());
 405         int bytes = (int)seq.byteSize();
 406         try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes);
 407              MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
 408             initializer.accept(heapArray.baseAddress());
 409             MemoryAddress.copy(heapArray.baseAddress(), nativeArray.baseAddress(), bytes);
 410             checker.accept(nativeArray.baseAddress());
 411         }
 412     }
 413 
 414     @Test(dataProvider="resizeOps")
 415     public void testCopyNativeToHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
 416         checkByteArrayAlignment(seq.elementLayout());
 417         int bytes = (int)seq.byteSize();
 418         try (MemorySegment nativeArray = MemorySegment.allocateNative(seq);
 419              MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
 420             initializer.accept(nativeArray.baseAddress());
 421             MemoryAddress.copy(nativeArray.baseAddress(), heapArray.baseAddress(), bytes);
 422             checker.accept(heapArray.baseAddress());
 423         }
 424     }
 425 
 426     @DataProvider(name = "bufferOps")
 427     public static Object[][] bufferOps() throws Throwable {
 428         return new Object[][]{
 429                 { (Function<ByteBuffer, Buffer>) bb -> bb, bufferMembers(ByteBuffer.class)},
 430                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asCharBuffer, bufferMembers(CharBuffer.class)},
 431                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asShortBuffer, bufferMembers(ShortBuffer.class)},
 432                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asIntBuffer, bufferMembers(IntBuffer.class)},
 433                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asFloatBuffer, bufferMembers(FloatBuffer.class)},
 434                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asLongBuffer, bufferMembers(LongBuffer.class)},
 435                 { (Function<ByteBuffer, Buffer>) ByteBuffer::asDoubleBuffer, bufferMembers(DoubleBuffer.class)},
 436         };
 437     }
 438 
 439     static Map<Method, Object[]> bufferMembers(Class<?> bufferClass) {
 440         Map<Method, Object[]> members = new HashMap<>();
 441         for (Method m : bufferClass.getMethods()) {
 442             //skip statics and method declared in j.l.Object
 443             if (m.getDeclaringClass().equals(Object.class) ||
 444                     (m.getModifiers() & Modifier.STATIC) != 0) continue;
 445             Object[] args = Stream.of(m.getParameterTypes())
 446                     .map(TestByteBuffer::defaultValue)
 447                     .toArray();
 448             members.put(m, args);
 449         }
 450         return members;
 451     }
 452 
 453     @DataProvider(name = "bufferHandleOps")
 454     public static Object[][] bufferHandleOps() throws Throwable {
 455         return new Object[][]{
 456                 { MethodHandles.byteBufferViewVarHandle(char[].class, ByteOrder.nativeOrder()) },
 457                 { MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()) },
 458                 { MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder()) },
 459                 { MethodHandles.byteBufferViewVarHandle(long[].class, ByteOrder.nativeOrder()) },
 460                 { MethodHandles.byteBufferViewVarHandle(float[].class, ByteOrder.nativeOrder()) },
 461                 { MethodHandles.byteBufferViewVarHandle(double[].class, ByteOrder.nativeOrder()) }
 462         };
 463     }
 464 
 465     static Map<MethodHandle, Object[]> varHandleMembers(ByteBuffer bb, VarHandle handle) {
 466         Map<MethodHandle, Object[]> members = new HashMap<>();
 467         for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
 468             Class<?>[] params = handle.accessModeType(mode).parameterArray();
 469             Object[] args = Stream.concat(Stream.of(bb), Stream.of(params).skip(1)
 470                     .map(TestByteBuffer::defaultValue))
 471                     .toArray();
 472             try {
 473                 members.put(MethodHandles.varHandleInvoker(mode, handle.accessModeType(mode)), args);
 474             } catch (Throwable ex) {
 475                 throw new AssertionError(ex);
 476             }
 477         }
 478         return members;
 479     }
 480 
 481     @DataProvider(name = "resizeOps")
 482     public Object[][] resizeOps() {
 483         Consumer<MemoryAddress> byteInitializer =
 484                 (base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
 485         Consumer<MemoryAddress> charInitializer =
 486                 (base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
 487         Consumer<MemoryAddress> shortInitializer =
 488                 (base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
 489         Consumer<MemoryAddress> intInitializer =
 490                 (base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
 491         Consumer<MemoryAddress> floatInitializer =
 492                 (base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
 493         Consumer<MemoryAddress> longInitializer =
 494                 (base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
 495         Consumer<MemoryAddress> doubleInitializer =
 496                 (base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
 497 
 498         Consumer<MemoryAddress> byteChecker =
 499                 (base) -> checkBytes(base, bytes, Function.identity(), byteHandle::get, ByteBuffer::get);
 500         Consumer<MemoryAddress> charChecker =
 501                 (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, charHandle::get, CharBuffer::get);
 502         Consumer<MemoryAddress> shortChecker =
 503                 (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, shortHandle::get, ShortBuffer::get);
 504         Consumer<MemoryAddress> intChecker =
 505                 (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, intHandle::get, IntBuffer::get);
 506         Consumer<MemoryAddress> floatChecker =
 507                 (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, floatHandle::get, FloatBuffer::get);
 508         Consumer<MemoryAddress> longChecker =
 509                 (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, longHandle::get, LongBuffer::get);
 510         Consumer<MemoryAddress> doubleChecker =
 511                 (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, doubleHandle::get, DoubleBuffer::get);
 512 
 513         return new Object[][]{
 514                 {byteChecker, byteInitializer, bytes},
 515                 {charChecker, charInitializer, chars},
 516                 {shortChecker, shortInitializer, shorts},
 517                 {intChecker, intInitializer, ints},
 518                 {floatChecker, floatInitializer, floats},
 519                 {longChecker, longInitializer, longs},
 520                 {doubleChecker, doubleInitializer, doubles}
 521         };
 522     }
 523 
 524     static Object defaultValue(Class<?> c) {
 525         if (c.isPrimitive()) {
 526             if (c == char.class) {
 527                 return (char)0;
 528             } else if (c == boolean.class) {
 529                 return false;
 530             } else if (c == byte.class) {
 531                 return (byte)0;
 532             } else if (c == short.class) {
 533                 return (short)0;
 534             } else if (c == int.class) {
 535                 return 0;
 536             } else if (c == long.class) {
 537                 return 0L;
 538             } else if (c == float.class) {
 539                 return 0f;
 540             } else if (c == double.class) {
 541                 return 0d;
 542             } else {
 543                 throw new IllegalStateException();
 544             }
 545         } else if (c.isArray()) {
 546             if (c == char[].class) {
 547                 return new char[1];
 548             } else if (c == boolean[].class) {
 549                 return new boolean[1];
 550             } else if (c == byte[].class) {
 551                 return new byte[1];
 552             } else if (c == short[].class) {
 553                 return new short[1];
 554             } else if (c == int[].class) {
 555                 return new int[1];
 556             } else if (c == long[].class) {
 557                 return new long[1];
 558             } else if (c == float[].class) {
 559                 return new float[1];
 560             } else if (c == double[].class) {
 561                 return new double[1];
 562             } else {
 563                 throw new IllegalStateException();
 564             }
 565         } else {
 566             return null;
 567         }
 568     }
 569 }