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