1 /*
   2  * Copyright (c) 2016, 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 import jdk.internal.misc.Unsafe;
  25 import java.lang.reflect.Field;
  26 
  27 /*
  28  * @test
  29  * @summary Test Unsafe.copySwapMemory
  30  * @modules java.base/jdk.internal.misc
  31  */
  32 public class CopySwap {
  33     private static final boolean DEBUG = Boolean.getBoolean("CopySwap.DEBUG");
  34 
  35     public static final long KB = 1024;
  36     public static final long MB = KB * 1024;
  37     public static final long GB = MB * 1024;
  38 
  39     private static final Unsafe UNSAFE;
  40     private static final int SMALL_COPY_SIZE = 32;
  41     private static final int BASE_ALIGNMENT = 16;
  42 
  43     static {
  44         try {
  45             Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
  46             f.setAccessible(true);
  47             UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
  48         } catch (Exception e) {
  49             throw new RuntimeException("Unable to get Unsafe instance.", e);
  50         }
  51     }
  52 
  53     private static long alignDown(long value, long alignment) {
  54         return value & ~(alignment - 1);
  55     }
  56 
  57     private static long alignUp(long value, long alignment) {
  58         return (value + alignment - 1) & ~(alignment - 1);
  59     }
  60 
  61     private static boolean isAligned(long value, long alignment) {
  62         return value == alignDown(value, alignment);
  63     }
  64 
  65     private CopySwap() {
  66     }
  67 
  68     /**
  69      * Generate verification data for a given offset
  70      *
  71      * The verification data is used to verify that the correct bytes
  72      * have indeed been copied and byte swapped.
  73      *
  74      * The data is generated based on the offset (in bytes) into the
  75      * source buffer. For a native buffer the offset is relative to
  76      * the base pointer. For a heap array it is relative to the
  77      * address of the first array element.
  78      *
  79      * This method will return the result of doing an elementSize byte
  80      * read starting at offset (in bytes).
  81      *
  82      * @param offset offset into buffer
  83      * @param elemSize size (in bytes) of the element
  84      *
  85      * @return the verification data, only the least significant
  86      * elemSize*8 bits are set, zero extended
  87      */
  88     private long getVerificationDataForOffset(long offset, long elemSize) {
  89         byte[] bytes = new byte[(int)elemSize];
  90 
  91         for (long i = 0; i < elemSize; i++) {
  92             bytes[(int)i] = (byte)(offset + i);
  93         }
  94 
  95         long o = UNSAFE.arrayBaseOffset(byte[].class);
  96 
  97         switch ((int)elemSize) {
  98         case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o));
  99         case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o));
 100         case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o));
 101         case 8: return UNSAFE.getLongUnaligned(bytes, o);
 102         default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
 103         }
 104     }
 105 
 106     /**
 107      * Verify byte swapped data
 108      *
 109      * @param ptr the data to verify
 110      * @param srcOffset the srcOffset (in bytes) from which the copy started,
 111      *        used as key to regenerate the verification data
 112      * @param dstOffset the offset (in bytes) in the array at which to start
 113      *        the verification, relative to the first element in the array
 114      * @param size size (in bytes) of data to to verify
 115      * @param elemSize size (in bytes) of the individual array elements
 116      *
 117      * @throws RuntimeException if an error is found
 118      */
 119     private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) {
 120         for (long offset = 0; offset < size; offset += elemSize) {
 121             long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize);
 122             long expected = byteSwap(expectedUnswapped, elemSize);
 123 
 124             long actual = getArrayElem(ptr, dstOffset + offset, elemSize);
 125 
 126             if (expected != actual) {
 127                 throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) +
 128                                            " dstOffset: 0x" + Long.toHexString(dstOffset) +
 129                                            " size: 0x" + Long.toHexString(size) +
 130                                            " offset: 0x" + Long.toHexString(offset) +
 131                                            " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) +
 132                                            " expected: 0x" + Long.toHexString(expected) +
 133                                            " != actual: 0x" + Long.toHexString(actual));
 134             }
 135         }
 136     }
 137 
 138     /**
 139      * Initialize an array with verification friendly data
 140      *
 141      * @param ptr pointer to the data to initialize
 142      * @param size size (in bytes) of the data
 143      * @param elemSize size (in bytes) of the individual elements
 144      */
 145     private void initVerificationData(GenericPointer ptr, long size, long elemSize) {
 146         for (long offset = 0; offset < size; offset++) {
 147             byte data = (byte)getVerificationDataForOffset(offset, 1);
 148 
 149             if (ptr.isOnHeap()) {
 150                 UNSAFE.putByte(ptr.getObject(), UNSAFE.arrayBaseOffset(ptr.getObject().getClass()) + offset, data);
 151             } else {
 152                 UNSAFE.putByte(ptr.getOffset() + offset, data);
 153             }
 154         }
 155     }
 156 
 157     /**
 158      * Allocate a primitive array
 159      *
 160      * @param size size (in bytes) of all the array elements (elemSize * length)
 161      * @param elemSize the size of the array elements
 162      *
 163      * @return a newly allocated primitive array
 164      */
 165     Object allocArray(long size, long elemSize) {
 166         int length = (int)(size / elemSize);
 167 
 168         switch ((int)elemSize) {
 169         case 2: return new short[length];
 170         case 4: return new int[length];
 171         case 8: return new long[length];
 172         default:
 173             throw new IllegalArgumentException("Invalid element size: " + elemSize);
 174         }
 175     }
 176 
 177     /**
 178      * Get the value of a primitive array entry
 179      *
 180      * @param ptr pointer to the data
 181      * @param offset offset (in bytes) of the array element, relative to the first element in the array
 182      *
 183      * @return the array element, as an unsigned long
 184      */
 185     private long getArrayElem(GenericPointer ptr, long offset, long elemSize) {
 186         if (ptr.isOnHeap()) {
 187             Object o = ptr.getObject();
 188             int index = (int)(offset / elemSize);
 189 
 190             if (o instanceof short[]) {
 191                 short[] arr = (short[])o;
 192                 return Short.toUnsignedLong(arr[index]);
 193             } else if (o instanceof int[]) {
 194                 int[] arr = (int[])o;
 195                 return Integer.toUnsignedLong(arr[index]);
 196             } else if (o instanceof long[]) {
 197                 long[] arr = (long[])o;
 198                 return arr[index];
 199             } else {
 200                 throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
 201             }
 202         } else {
 203             long addr = ptr.getOffset() + offset;
 204 
 205             switch ((int)elemSize) {
 206             case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr));
 207             case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr));
 208             case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr));
 209             case 8: return UNSAFE.getLongUnaligned(null, addr);
 210             default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
 211             }
 212         }
 213     }
 214 
 215     private void putValue(long addr, long elemSize, long value) {
 216         switch ((int)elemSize) {
 217         case 1: UNSAFE.putByte(addr, (byte)value); break;
 218         case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break;
 219         case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break;
 220         case 8: UNSAFE.putLongUnaligned(null, addr, value); break;
 221         default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
 222         }
 223     }
 224 
 225     /**
 226      * Get the size of the elements for an array
 227      *
 228      * @param o a primitive heap array
 229      *
 230      * @return the size (in bytes) of the individual array elements
 231      */
 232     private long getArrayElemSize(Object o) {
 233         if (o instanceof short[]) {
 234             return 2;
 235         } else if (o instanceof int[]) {
 236             return 4;
 237         } else if (o instanceof long[]) {
 238             return 8;
 239         } else {
 240             throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
 241         }
 242     }
 243 
 244     /**
 245      * Byte swap a value
 246      *
 247      * @param value the value to swap, only the bytes*8 least significant bits are used
 248      * @param size size (in bytes) of the value
 249      *
 250      * @return the byte swapped value in the bytes*8 least significant bits
 251      */
 252     private long byteSwap(long value, long size) {
 253         switch ((int)size) {
 254         case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value));
 255         case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value));
 256         case 8: return Long.reverseBytes(value);
 257         default: throw new IllegalArgumentException("Invalid element size: " + size);
 258         }
 259     }
 260 
 261     /**
 262      * Verify data in a heap array which has *not* been byte swapped
 263      *
 264      * @param ptr the data to verify
 265      * @param startOffset the offset (in bytes) at which to start the verification
 266      * @param size size (in bytes) of the data to verify
 267      *
 268      * @throws RuntimeException if an error is found
 269      */
 270     private void verifyUnswappedData(GenericPointer ptr, long startOffset, long size) {
 271         for (long elemOffset = startOffset; elemOffset < startOffset + size; elemOffset++) {
 272             byte expected = (byte)getVerificationDataForOffset(elemOffset, 1);
 273 
 274             byte actual;
 275             if (ptr.isOnHeap()) {
 276                 actual = UNSAFE.getByte(ptr.getObject(), UNSAFE.arrayBaseOffset(ptr.getObject().getClass()) + elemOffset);
 277             } else {
 278                 actual = UNSAFE.getByte(ptr.getOffset() + elemOffset);
 279             }
 280 
 281             if (expected != actual) {
 282                 throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) +
 283                                            " size: 0x" + Long.toHexString(size) +
 284                                            " elemOffset: 0x" + Long.toHexString(elemOffset) +
 285                                            " expected: 0x" + Long.toHexString(expected) +
 286                                            " != actual: 0x" + Long.toHexString(actual));
 287             }
 288         }
 289     }
 290 
 291     
 292     /**
 293      * Copy and byte swap data from the source to the destination
 294      *
 295      * This method will pre-populate the whole source and destination
 296      * buffers with verification friendly data. It will then use
 297      * copySwapMemory to fill part of the destination buffer with
 298      * swapped data from the source. Some space (padding) will be
 299      * left before and after the data in the destination buffer, which
 300      * should not be touched/overwritten by the copy call.
 301      *
 302      * Note: Both source and destination buffers will be overwritten!
 303      *
 304      * @param src source buffer to copy from
 305      * @param srcOffset the offset (in bytes) in the source buffer, relative to
 306      *        the first array element, at which to start reading data
 307      * @param dst destination buffer to copy to
 308      * @param dstOffset the offset (in bytes) in the destination
 309      *        buffer, relative to the first array element, at which to
 310      *        start writing data
 311      * @param bufSize the size (in bytes) of the src and dst arrays
 312      * @param copyBytes the size (in bytes) of the copy to perform,
 313      *        must be a multiple of elemSize
 314      * @param elemSize the size (in bytes) of the elements to byte swap
 315      *
 316      * @throws RuntimeException if an error is found
 317      */
 318     private void testCopySwap(GenericPointer src, long srcOffset,
 319                               GenericPointer dst, long dstOffset,
 320                               long bufSize, long copyBytes, long elemSize) {
 321         if (!isAligned(copyBytes, elemSize)) {
 322             throw new IllegalArgumentException(
 323                 "copyBytes (" + copyBytes + ") must be a multiple of elemSize (" + elemSize + ")");
 324         }
 325         if (src.isOnHeap() && !isAligned(srcOffset, elemSize)) {
 326             throw new IllegalArgumentException(
 327                 "srcOffset (" + srcOffset + ") must be a multiple of elemSize (" + elemSize + ")");
 328         }
 329         if (dst.isOnHeap() && !isAligned(dstOffset, elemSize)) {
 330             throw new IllegalArgumentException(
 331                 "dstOffset (" + dstOffset + ") must be a multiple of elemSize (" + elemSize + ")");
 332         }
 333         if (srcOffset + copyBytes > bufSize) {
 334             throw new IllegalArgumentException(
 335                 "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
 336         }
 337         if (dstOffset + copyBytes > bufSize) {
 338             throw new IllegalArgumentException(
 339                 "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
 340         }
 341 
 342         // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes)
 343         initVerificationData(src, bufSize, elemSize);
 344         if (!src.equals(dst)) {
 345             initVerificationData(dst, bufSize, elemSize);
 346         }
 347 
 348         if (DEBUG) {
 349             System.out.println("===before===");
 350             for (int offset = 0; offset < bufSize; offset += elemSize) {
 351                 long srcValue = getArrayElem(src, offset, elemSize);
 352                 long dstValue = getArrayElem(dst, offset, elemSize);
 353 
 354                 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
 355                                  " src=0x" + Long.toHexString(srcValue) +
 356                                  " dst=0x" + Long.toHexString(dstValue));
 357             }
 358         }
 359 
 360         // Copy & swap data into the middle of the destination buffer
 361         UNSAFE.copySwapMemory(src.getObject(),
 362                               (src.isOnHeap() ? UNSAFE.arrayBaseOffset(src.getObject().getClass()) : src.getOffset()) + srcOffset,
 363                               dst.getObject(),
 364                               (dst.isOnHeap() ? UNSAFE.arrayBaseOffset(dst.getObject().getClass()) : dst.getOffset()) + dstOffset,
 365                               copyBytes,
 366                               elemSize);
 367 
 368         if (DEBUG) {
 369             System.out.println("===after===");
 370             for (int offset = 0; offset < bufSize; offset += elemSize) {
 371                 long srcValue = getArrayElem(src, offset, elemSize);
 372                 long dstValue = getArrayElem(dst, offset, elemSize);
 373 
 374                 System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
 375                                  " src=0x" + Long.toHexString(srcValue) +
 376                                  " dst=0x" + Long.toHexString(dstValue));
 377             }
 378         }
 379 
 380         // Verify the the front padding is unchanged
 381         verifyUnswappedData(dst, 0, dstOffset);
 382 
 383         // Verify swapped data
 384         verifySwappedData(dst, srcOffset, dstOffset, copyBytes, elemSize);
 385 
 386         // Verify that the back back padding is unchanged
 387         long frontAndDataBytes = dstOffset + copyBytes;
 388         long trailingBytes = bufSize - frontAndDataBytes;
 389         verifyUnswappedData(dst, frontAndDataBytes, trailingBytes);
 390     }
 391 
 392     /**
 393      * Test various configurations copy-swapping from one buffer to the other
 394      *
 395      * @param src the source buffer to copy from
 396      * @param dst the destination buffer to copy to
 397      * @param size size (in bytes) of the buffers
 398      * @param elemSize size (in bytes) of the individual elements
 399      *
 400      * @throws RuntimeException if an error is found
 401      */
 402     public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize) {
 403         // offset in source from which to start reading data
 404         for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) {
 405 
 406             // offset in destination at which to start writing data
 407             for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) {
 408 
 409                 // number of bytes to copy
 410                 long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset);
 411                 for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) {
 412                     try {
 413                         testCopySwap(src, srcOffset, dst, dstOffset, size, copyBytes, elemSize);
 414                     } catch (RuntimeException e) {
 415                         // Wrap the exception in another exception to catch the relevant configuration data
 416                         throw new RuntimeException("testBufferPair: " +
 417                                                    "src=" + src +
 418                                                    " dst=" + dst +
 419                                                    " elemSize=0x" + Long.toHexString(elemSize) +
 420                                                    " copyBytes=0x" + Long.toHexString(copyBytes) +
 421                                                    " srcOffset=0x" + Long.toHexString(srcOffset) +
 422                                                    " dstOffset=0x" + Long.toHexString(dstOffset),
 423                                                    e);
 424                     }
 425                 }
 426             }
 427         }
 428     }
 429 
 430     /**
 431      * Test copying between various permutations of buffers
 432      *
 433      * @param buffers buffers to permute (src x dst)
 434      * @param size size (in bytes) of buffers
 435      * @param elemSize size (in bytes) of individual elements
 436      *
 437      * @throws RuntimeException if an error is found
 438      */
 439     public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize) {
 440         for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) {
 441             for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) {
 442                 testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize);
 443             }
 444         }
 445     }
 446 
 447     /**
 448      * Test copying of a specific element size
 449      *
 450      * @param size size (in bytes) of buffers to allocate
 451      * @param elemSize size (in bytes) of individual elements
 452      *
 453      * @throws RuntimeException if an error is found
 454      */
 455     private void testElemSize(long size, long elemSize) {
 456         long buf1Raw = 0;
 457         long buf2Raw = 0;
 458 
 459         try {
 460             buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
 461             long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT);
 462 
 463             buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
 464             long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT);
 465 
 466             GenericPointer[] buffers = {
 467                 new GenericPointer(buf1),
 468                 new GenericPointer(buf2),
 469                 new GenericPointer(allocArray(size, elemSize)),
 470                 new GenericPointer(allocArray(size, elemSize))
 471             };
 472 
 473             testPermuteBuffers(buffers, size, elemSize);
 474         } finally {
 475             if (buf1Raw != 0) {
 476                 UNSAFE.freeMemory(buf1Raw);
 477             }
 478             if (buf2Raw != 0) {
 479                 UNSAFE.freeMemory(buf2Raw);
 480             }
 481         }
 482     }
 483 
 484     /**
 485      * Verify that small copy swaps work
 486      */
 487     private void testSmallCopy() {
 488         int smallBufSize = SMALL_COPY_SIZE;
 489 
 490         // Test various element types and heap/native combinations
 491         for (long elemSize = 2; elemSize <= 8; elemSize <<= 1) {
 492             testElemSize(smallBufSize, elemSize);
 493         }
 494     }
 495 
 496 
 497     /**
 498      * Verify that large copy swaps work
 499      */
 500     private void testLargeCopy() {
 501         long size = 2 * GB + 8;
 502         long bufRaw = 0;
 503 
 504         // Check that a large native copy succeeds
 505         try {
 506             try {
 507                 bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
 508             } catch (OutOfMemoryError e) {
 509                 // Accept failure, skip test
 510                 return;
 511             }
 512 
 513             long buf = alignUp(bufRaw, BASE_ALIGNMENT);
 514 
 515             UNSAFE.copySwapMemory(null, buf, null, buf, size, 8);
 516         } catch (Exception e) {
 517             throw new RuntimeException("copySwapMemory of large buffer failed");
 518         } finally {
 519             if (bufRaw != 0) {
 520                 UNSAFE.freeMemory(bufRaw);
 521             }
 522         }
 523     }
 524 
 525     /**
 526      * Run positive tests
 527      *
 528      * @throws RuntimeException if an error is found
 529      */
 530     private void testPositive() {
 531         testSmallCopy();
 532         testLargeCopy();
 533     }
 534 
 535     /**
 536      * Run negative tests, testing corner cases and the various exceptions
 537      *
 538      * @throws RuntimeException if an error is found
 539      */
 540     private void testNegative() {
 541         long bufRaw = 0;
 542 
 543         try {
 544             bufRaw = UNSAFE.allocateMemory(1024);
 545             long buf = alignUp(bufRaw, BASE_ALIGNMENT);
 546 
 547             for (int elemSize = 2; elemSize <= 8; elemSize <<= 1) {
 548                 long[] illegalSizes = { -1, 1, elemSize - 1, elemSize + 1, elemSize * 2 - 1 };
 549                 for (long size : illegalSizes) {
 550                     try {
 551                         // Check that elemSize 1 throws an IAE
 552                         UNSAFE.copySwapMemory(null, buf, null, buf, size, elemSize);
 553                         throw new RuntimeException("copySwapMemory failed to throw IAE for size=" + size + " elemSize=" + elemSize);
 554                     } catch (IllegalArgumentException e) {
 555                         // good
 556                     }
 557                 }
 558             }
 559 
 560             long illegalElemSizes[] = { 0, 1, 3, 5, 6, 7, 9, 10, -1 };
 561             for (long elemSize : illegalElemSizes) {
 562                 try {
 563                     // Check that elemSize 1 throws an IAE
 564                     UNSAFE.copySwapMemory(null, buf, null, buf, 16, elemSize);
 565                     throw new RuntimeException("copySwapMemory failed to throw NPE");
 566                 } catch (IllegalArgumentException e) {
 567                     // good
 568                 }
 569             }
 570 
 571             try {
 572                 // Check that a NULL source throws NPE
 573                 UNSAFE.copySwapMemory(null, 0, null, buf, 16, 2);
 574                 throw new RuntimeException("copySwapMemory failed to throw NPE");
 575             } catch (NullPointerException e) {
 576                 // good
 577             }
 578 
 579             try {
 580                 // Check that a NULL destination throws NPE
 581                 UNSAFE.copySwapMemory(null, buf, null, 0, 16, 2);
 582                 throw new RuntimeException("copySwapMemory failed to throw NPE");
 583             } catch (NullPointerException e) {
 584                 // good
 585             }
 586 
 587             try {
 588                 // Check that a reference array destination throws IAE
 589                 UNSAFE.copySwapMemory(null, buf, new Object[16], UNSAFE.arrayBaseOffset(Object[].class), 16, 8);
 590                 throw new RuntimeException("copySwapMemory failed to throw NPE");
 591             } catch (IllegalArgumentException e) {
 592                 // good
 593             }
 594         } finally {
 595             if (bufRaw != 0) {
 596                 UNSAFE.freeMemory(bufRaw);
 597             }
 598         }
 599     }
 600 
 601     /**
 602      * Run all tests
 603      *
 604      * @throws RuntimeException if an error is found
 605      */
 606     private void test() {
 607         testPositive();
 608         testNegative();
 609     }
 610 
 611     public static void main(String[] args) {
 612         CopySwap cs = new CopySwap();
 613         cs.test();
 614     }
 615 
 616     /**
 617      * Helper class to represent a "pointer" - either a heap array or
 618      * a pointer to a native buffer.
 619      *
 620      * Note: this only represents the base object or pointer, not an
 621      * offset into it. That is, only one of the "o" and "offset"
 622      * fields is ever used, never both.
 623     /*/
 624     static class GenericPointer {
 625         private final Object o;
 626         private final long offset;
 627 
 628         private GenericPointer(Object o, long offset) {
 629             this.o = o;
 630             this.offset = offset;
 631         }
 632 
 633         public String toString() {
 634             return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")";
 635         }
 636 
 637         public boolean equals(Object other) {
 638             if (!(other instanceof GenericPointer)) {
 639                 return false;
 640             }
 641 
 642             GenericPointer otherp = (GenericPointer)other;
 643 
 644             return o == otherp.o && offset == otherp.offset;
 645         }
 646 
 647         GenericPointer(Object o) {
 648             this(o, 0);
 649         }
 650 
 651         GenericPointer(long offset) {
 652             this(null, offset);
 653         }
 654 
 655         public boolean isOnHeap() {
 656             return o != null;
 657         }
 658 
 659         public Object getObject() {
 660             return o;
 661         }
 662 
 663         public long getOffset() {
 664             return offset;
 665         }
 666     }
 667 }