changeset 13921:b0001f7f8765

8151233: Unify CopySwap and CopyMemory tests Reviewed-by: dholmes
author mikael
date Wed, 16 Mar 2016 14:04:12 -0700
parents 28df1af8e872
children 90751bfb53cd
files test/jdk/internal/misc/Unsafe/CopyCommon.java test/jdk/internal/misc/Unsafe/CopyMemory.java test/jdk/internal/misc/Unsafe/CopySwap.java
diffstat 3 files changed, 615 insertions(+), 1081 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/internal/misc/Unsafe/CopyCommon.java	Wed Mar 16 14:04:12 2016 -0700
@@ -0,0 +1,607 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import jdk.internal.misc.Unsafe;
+import java.lang.reflect.Field;
+
+/**
+ * Helper class to support testing of Unsafe.copyMemory and Unsafe.copySwapMemory
+ */
+public class CopyCommon {
+    private static final boolean DEBUG = Boolean.getBoolean("CopyCommon.DEBUG");
+
+    public static final long KB = 1024;
+    public static final long MB = KB * 1024;
+    public static final long GB = MB * 1024;
+
+    static final int SMALL_COPY_SIZE = 32;
+    static final int BASE_ALIGNMENT = 16;
+
+    protected static final Unsafe UNSAFE;
+
+    static {
+        try {
+            Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
+            f.setAccessible(true);
+            UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
+        } catch (Exception e) {
+            throw new RuntimeException("Unable to get Unsafe instance.", e);
+        }
+    }
+
+    static long alignDown(long value, long alignment) {
+        return value & ~(alignment - 1);
+    }
+
+    static long alignUp(long value, long alignment) {
+        return (value + alignment - 1) & ~(alignment - 1);
+    }
+
+    static boolean isAligned(long value, long alignment) {
+        return value == alignDown(value, alignment);
+    }
+
+    CopyCommon() {
+    }
+
+    /**
+     * Generate verification data for a given offset
+     *
+     * The verification data is used to verify that the correct bytes
+     * have indeed been copied and byte swapped.
+     *
+     * The data is generated based on the offset (in bytes) into the
+     * source buffer. For a native buffer the offset is relative to
+     * the base pointer. For a heap array it is relative to the
+     * address of the first array element.
+     *
+     * This method will return the result of doing an elementSize byte
+     * read starting at offset (in bytes).
+     *
+     * @param offset offset into buffer
+     * @param elemSize size (in bytes) of the element
+     *
+     * @return the verification data, only the least significant
+     * elemSize*8 bits are set, zero extended
+     */
+    private long getVerificationDataForOffset(long offset, long elemSize) {
+        byte[] bytes = new byte[(int)elemSize];
+
+        for (long i = 0; i < elemSize; i++) {
+            bytes[(int)i] = (byte)(offset + i);
+        }
+
+        long o = UNSAFE.arrayBaseOffset(byte[].class);
+
+        switch ((int)elemSize) {
+        case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o));
+        case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o));
+        case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o));
+        case 8: return UNSAFE.getLongUnaligned(bytes, o);
+        default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
+        }
+    }
+
+    /**
+     * Verify byte swapped data
+     *
+     * @param ptr the data to verify
+     * @param srcOffset the srcOffset (in bytes) from which the copy started,
+     *        used as key to regenerate the verification data
+     * @param dstOffset the offset (in bytes) in the array at which to start
+     *        the verification, relative to the first element in the array
+     * @param size size (in bytes) of data to to verify
+     * @param elemSize size (in bytes) of the individual array elements
+     *
+     * @throws RuntimeException if an error is found
+     */
+    private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) {
+        for (long offset = 0; offset < size; offset += elemSize) {
+            long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize);
+            long expected = byteSwap(expectedUnswapped, elemSize);
+
+            long actual = getArrayElem(ptr, dstOffset + offset, elemSize);
+
+            if (expected != actual) {
+                throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) +
+                                           " dstOffset: 0x" + Long.toHexString(dstOffset) +
+                                           " size: 0x" + Long.toHexString(size) +
+                                           " offset: 0x" + Long.toHexString(offset) +
+                                           " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) +
+                                           " expected: 0x" + Long.toHexString(expected) +
+                                           " != actual: 0x" + Long.toHexString(actual));
+            }
+        }
+    }
+
+    /**
+     * Initialize an array with verification friendly data
+     *
+     * @param ptr pointer to the data to initialize
+     * @param size size (in bytes) of the data
+     * @param elemSize size (in bytes) of the individual elements
+     */
+    private void initVerificationData(GenericPointer ptr, long size, long elemSize) {
+        for (long offset = 0; offset < size; offset++) {
+            byte data = (byte)getVerificationDataForOffset(offset, 1);
+
+            if (ptr.isOnHeap()) {
+                UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data);
+            } else {
+                UNSAFE.putByte(ptr.getOffset() + offset, data);
+            }
+        }
+    }
+
+    /**
+     * Allocate a primitive array
+     *
+     * @param size size (in bytes) of all the array elements (elemSize * length)
+     * @param elemSize the size of the array elements
+     *
+     * @return a newly allocated primitive array
+     */
+    Object allocArray(long size, long elemSize) {
+        int length = (int)(size / elemSize);
+
+        switch ((int)elemSize) {
+        case 1: return new byte[length];
+        case 2: return new short[length];
+        case 4: return new int[length];
+        case 8: return new long[length];
+        default:
+            throw new IllegalArgumentException("Invalid element size: " + elemSize);
+        }
+    }
+
+    /**
+     * Get the value of a primitive array entry
+     *
+     * @param ptr pointer to the data
+     * @param offset offset (in bytes) of the array element, relative to the first element in the array
+     *
+     * @return the array element, as an unsigned long
+     */
+    private long getArrayElem(GenericPointer ptr, long offset, long elemSize) {
+        if (ptr.isOnHeap()) {
+            Object o = ptr.getObject();
+            int index = (int)(offset / elemSize);
+
+            if (o instanceof short[]) {
+                short[] arr = (short[])o;
+                return Short.toUnsignedLong(arr[index]);
+            } else if (o instanceof int[]) {
+                int[] arr = (int[])o;
+                return Integer.toUnsignedLong(arr[index]);
+            } else if (o instanceof long[]) {
+                long[] arr = (long[])o;
+                return arr[index];
+            } else {
+                throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
+            }
+        } else {
+            long addr = ptr.getOffset() + offset;
+
+            switch ((int)elemSize) {
+            case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr));
+            case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr));
+            case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr));
+            case 8: return UNSAFE.getLongUnaligned(null, addr);
+            default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
+            }
+        }
+    }
+
+    private void putValue(long addr, long elemSize, long value) {
+        switch ((int)elemSize) {
+        case 1: UNSAFE.putByte(addr, (byte)value); break;
+        case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break;
+        case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break;
+        case 8: UNSAFE.putLongUnaligned(null, addr, value); break;
+        default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
+        }
+    }
+
+    /**
+     * Get the size of the elements for an array
+     *
+     * @param o a primitive heap array
+     *
+     * @return the size (in bytes) of the individual array elements
+     */
+    private long getArrayElemSize(Object o) {
+        if (o instanceof short[]) {
+            return 2;
+        } else if (o instanceof int[]) {
+            return 4;
+        } else if (o instanceof long[]) {
+            return 8;
+        } else {
+            throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
+        }
+    }
+
+    /**
+     * Byte swap a value
+     *
+     * @param value the value to swap, only the bytes*8 least significant bits are used
+     * @param size size (in bytes) of the value
+     *
+     * @return the byte swapped value in the bytes*8 least significant bits
+     */
+    private long byteSwap(long value, long size) {
+        switch ((int)size) {
+        case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value));
+        case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value));
+        case 8: return Long.reverseBytes(value);
+        default: throw new IllegalArgumentException("Invalid element size: " + size);
+        }
+    }
+
+    /**
+     * Verify data which has *not* been byte swapped
+     *
+     * @param ptr the data to verify
+     * @param startOffset the offset (in bytes) at which to start the verification
+     * @param size size (in bytes) of the data to verify
+     *
+     * @throws RuntimeException if an error is found
+     */
+    private void verifyUnswappedData(GenericPointer ptr, long startOffset, long srcOffset, long size) {
+        for (long i = 0; i < size; i++) {
+            byte expected = (byte)getVerificationDataForOffset(srcOffset + i, 1);
+
+            byte actual;
+            if (ptr.isOnHeap()) {
+                actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + startOffset + i);
+            } else {
+                actual = UNSAFE.getByte(ptr.getOffset() + startOffset + i);
+            }
+
+            if (expected != actual) {
+                throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) +
+                                           " srcOffset: 0x" + Long.toHexString(srcOffset) +
+                                           " size: 0x" + Long.toHexString(size) +
+                                           " i: 0x" + Long.toHexString(i) +
+                                           " expected: 0x" + Long.toHexString(expected) +
+                                           " != actual: 0x" + Long.toHexString(actual));
+            }
+        }
+    }
+
+
+    /**
+     * Copy and byte swap data from the source to the destination
+     *
+     * This method will pre-populate the whole source and destination
+     * buffers with verification friendly data. It will then copy data
+     * to fill part of the destination buffer with data from the
+     * source, optionally byte swapping the copied elements on the
+     * fly. Some space (padding) will be left before and after the
+     * data in the destination buffer, which should not be
+     * touched/overwritten by the copy call.
+     *
+     * Note: Both source and destination buffers will be overwritten!
+     *
+     * @param src source buffer to copy from
+     * @param srcOffset the offset (in bytes) in the source buffer, relative to
+     *        the first array element, at which to start reading data
+     * @param dst destination buffer to copy to
+     * @param dstOffset the offset (in bytes) in the destination
+     *        buffer, relative to the first array element, at which to
+     *        start writing data
+     * @param bufSize the size (in bytes) of the src and dst arrays
+     * @param copyBytes the size (in bytes) of the copy to perform,
+     *        must be a multiple of elemSize
+     * @param elemSize the size (in bytes) of the elements
+     * @param swap true if elements should be byte swapped
+     *
+     * @throws RuntimeException if an error is found
+     */
+    void testCopyGeneric(GenericPointer src, long srcOffset,
+                         GenericPointer dst, long dstOffset,
+                         long bufSize, long copyBytes, long elemSize, boolean swap) {
+        if (swap) {
+            if (!isAligned(copyBytes, elemSize)) {
+                throw new IllegalArgumentException(
+                    "copyBytes (" + copyBytes + ") must be a multiple of elemSize (" + elemSize + ")");
+            }
+            if (src.isOnHeap() && !isAligned(srcOffset, elemSize)) {
+                throw new IllegalArgumentException(
+                    "srcOffset (" + srcOffset + ") must be a multiple of elemSize (" + elemSize + ")");
+            }
+            if (dst.isOnHeap() && !isAligned(dstOffset, elemSize)) {
+                throw new IllegalArgumentException(
+                    "dstOffset (" + dstOffset + ") must be a multiple of elemSize (" + elemSize + ")");
+            }
+        }
+
+        if (srcOffset + copyBytes > bufSize) {
+            throw new IllegalArgumentException(
+                "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
+        }
+        if (dstOffset + copyBytes > bufSize) {
+            throw new IllegalArgumentException(
+                "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
+        }
+
+        // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes)
+        initVerificationData(src, bufSize, elemSize);
+        if (!src.equals(dst)) {
+            initVerificationData(dst, bufSize, elemSize);
+        }
+
+        if (DEBUG) {
+            System.out.println("===before===");
+            for (int offset = 0; offset < bufSize; offset += elemSize) {
+                long srcValue = getArrayElem(src, offset, elemSize);
+                long dstValue = getArrayElem(dst, offset, elemSize);
+
+                System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
+                                 " src=0x" + Long.toHexString(srcValue) +
+                                 " dst=0x" + Long.toHexString(dstValue));
+            }
+        }
+
+        if (swap) {
+            // Copy & swap data into the middle of the destination buffer
+            UNSAFE.copySwapMemory(src.getObject(),
+                                  src.getOffset() + srcOffset,
+                                  dst.getObject(),
+                                  dst.getOffset() + dstOffset,
+                                  copyBytes,
+                                  elemSize);
+        } else {
+            // Copy & swap data into the middle of the destination buffer
+            UNSAFE.copyMemory(src.getObject(),
+                              src.getOffset() + srcOffset,
+                              dst.getObject(),
+                              dst.getOffset() + dstOffset,
+                              copyBytes);
+        }
+
+        if (DEBUG) {
+            System.out.println("===after===");
+            for (int offset = 0; offset < bufSize; offset += elemSize) {
+                long srcValue = getArrayElem(src, offset, elemSize);
+                long dstValue = getArrayElem(dst, offset, elemSize);
+
+                System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
+                                 " src=0x" + Long.toHexString(srcValue) +
+                                 " dst=0x" + Long.toHexString(dstValue));
+            }
+        }
+
+        // Verify the the front padding is unchanged
+        verifyUnswappedData(dst, 0, 0, dstOffset);
+
+        if (swap) {
+            // Verify swapped data
+            verifySwappedData(dst, srcOffset, dstOffset, copyBytes, elemSize);
+        } else {
+            // Verify copied/unswapped data
+            verifyUnswappedData(dst, dstOffset, srcOffset, copyBytes);
+        }
+
+        // Verify that the back padding is unchanged
+        long frontAndCopyBytes = dstOffset + copyBytes;
+        long trailingBytes = bufSize - frontAndCopyBytes;
+        verifyUnswappedData(dst, frontAndCopyBytes, frontAndCopyBytes, trailingBytes);
+    }
+
+    /**
+     * Test various configurations of copying and optionally swapping data
+     *
+     * @param src the source buffer to copy from
+     * @param dst the destination buffer to copy to
+     * @param size size (in bytes) of the buffers
+     * @param elemSize size (in bytes) of the individual elements
+     *
+     * @throws RuntimeException if an error is found
+     */
+    public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize, boolean swap) {
+        // offset in source from which to start reading data
+        for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) {
+
+            // offset in destination at which to start writing data
+            for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) {
+
+                // number of bytes to copy
+                long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset);
+                for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) {
+                    try {
+                        testCopyGeneric(src, srcOffset, dst, dstOffset, size, copyBytes, elemSize, swap);
+                    } catch (RuntimeException e) {
+                        // Wrap the exception in another exception to catch the relevant configuration data
+                        throw new RuntimeException("testBufferPair: " +
+                                                   "src=" + src +
+                                                   " dst=" + dst +
+                                                   " elemSize=0x" + Long.toHexString(elemSize) +
+                                                   " copyBytes=0x" + Long.toHexString(copyBytes) +
+                                                   " srcOffset=0x" + Long.toHexString(srcOffset) +
+                                                   " dstOffset=0x" + Long.toHexString(dstOffset) +
+                                                   " swap=" + swap,
+                                                   e);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Test copying between various permutations of buffers
+     *
+     * @param buffers buffers to permute (src x dst)
+     * @param size size (in bytes) of buffers
+     * @param elemSize size (in bytes) of individual elements
+     *
+     * @throws RuntimeException if an error is found
+     */
+    public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize, boolean swap) {
+        System.out.println("testPermuteBuffers(buffers, " + size + ", " + elemSize + ", " + swap + ")");
+        for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) {
+            for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) {
+                testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize, swap);
+            }
+        }
+    }
+
+    /**
+     * Test copying of a specific element size
+     *
+     * @param size size (in bytes) of buffers to allocate
+     * @param elemSize size (in bytes) of individual elements
+     *
+     * @throws RuntimeException if an error is found
+     */
+    private void testElemSize(long size, long elemSize, boolean swap) {
+        long buf1Raw = 0;
+        long buf2Raw = 0;
+
+        try {
+            buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
+            long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT);
+
+            buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
+            long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT);
+
+            GenericPointer[] buffers = {
+                new GenericPointer(buf1),
+                new GenericPointer(buf2),
+                new GenericPointer(allocArray(size, elemSize)),
+                new GenericPointer(allocArray(size, elemSize))
+            };
+
+            testPermuteBuffers(buffers, size, elemSize, swap);
+        } finally {
+            if (buf1Raw != 0) {
+                UNSAFE.freeMemory(buf1Raw);
+            }
+            if (buf2Raw != 0) {
+                UNSAFE.freeMemory(buf2Raw);
+            }
+        }
+    }
+
+    /**
+     * Verify that small copies work
+     */
+    void testSmallCopy(boolean swap) {
+        int smallBufSize = SMALL_COPY_SIZE;
+        int minElemSize = swap ? 2 : 1;
+        int maxElemSize = swap ? 8 : 1;
+
+        // Test various element types and heap/native combinations
+        for (long elemSize = minElemSize; elemSize <= maxElemSize; elemSize <<= 1) {
+            testElemSize(smallBufSize, elemSize, swap);
+        }
+    }
+
+
+    /**
+     * Verify that large copies work
+     */
+    void testLargeCopy(boolean swap) {
+        long size = 2 * GB + 8;
+        long bufRaw = 0;
+
+        // Check that a large native copy succeeds
+        try {
+            try {
+                bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
+            } catch (OutOfMemoryError e) {
+                // Accept failure, skip test
+                return;
+            }
+
+            long buf = alignUp(bufRaw, BASE_ALIGNMENT);
+
+            if (swap) {
+                UNSAFE.copySwapMemory(null, buf, null, buf, size, 8);
+            } else {
+                UNSAFE.copyMemory(null, buf, null, buf, size);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("copy of large buffer failed (swap=" + swap + ")");
+        } finally {
+            if (bufRaw != 0) {
+                UNSAFE.freeMemory(bufRaw);
+            }
+        }
+    }
+
+    /**
+     * Helper class to represent a "pointer" - either a heap array or
+     * a pointer to a native buffer.
+     *
+     * In the case of a native pointer, the Object is null and the offset is
+     * the absolute address of the native buffer.
+     *
+     * In the case of a heap object, the Object is a primitive array, and
+     * the offset will be set to the base offset to the first element, meaning
+     * the object and the offset together form a double-register pointer.
+     */
+    static class GenericPointer {
+        private final Object o;
+        private final long offset;
+
+        private GenericPointer(Object o, long offset) {
+            this.o = o;
+            this.offset = offset;
+        }
+
+        public String toString() {
+            return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")";
+        }
+
+        public boolean equals(Object other) {
+            if (!(other instanceof GenericPointer)) {
+                return false;
+            }
+
+            GenericPointer otherp = (GenericPointer)other;
+
+            return o == otherp.o && offset == otherp.offset;
+        }
+
+        GenericPointer(Object o) {
+            this(o, UNSAFE.arrayBaseOffset(o.getClass()));
+        }
+
+        GenericPointer(long offset) {
+            this(null, offset);
+        }
+
+        public boolean isOnHeap() {
+            return o != null;
+        }
+
+        public Object getObject() {
+            return o;
+        }
+
+        public long getOffset() {
+            return offset;
+        }
+    }
+}
--- a/test/jdk/internal/misc/Unsafe/CopyMemory.java	Wed Mar 16 13:16:14 2016 -0400
+++ b/test/jdk/internal/misc/Unsafe/CopyMemory.java	Wed Mar 16 14:04:12 2016 -0700
@@ -29,492 +29,18 @@
  * @summary Test Unsafe.copyMemory
  * @modules java.base/jdk.internal.misc
  */
-public class CopyMemory {
-    private static final boolean DEBUG = Boolean.getBoolean("CopyMemory.DEBUG");
-
-    public static final long KB = 1024;
-    public static final long MB = KB * 1024;
-    public static final long GB = MB * 1024;
-
-    private static final Unsafe UNSAFE;
-    private static final int SMALL_COPY_SIZE = 32;
-    private static final int BASE_ALIGNMENT = 16;
-
-    static {
-        try {
-            Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
-            f.setAccessible(true);
-            UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
-        } catch (Exception e) {
-            throw new RuntimeException("Unable to get Unsafe instance.", e);
-        }
-    }
-
-    private static long alignDown(long value, long alignment) {
-        return value & ~(alignment - 1);
-    }
-
-    private static long alignUp(long value, long alignment) {
-        return (value + alignment - 1) & ~(alignment - 1);
-    }
-
-    private static boolean isAligned(long value, long alignment) {
-        return value == alignDown(value, alignment);
-    }
-
+public class CopyMemory extends CopyCommon {
     private CopyMemory() {
     }
 
     /**
-     * Generate verification data for a given offset
-     *
-     * The verification data is used to verify that the correct bytes
-     * have indeed been copied and byte swapped.
-     *
-     * The data is generated based on the offset (in bytes) into the
-     * source buffer. For a native buffer the offset is relative to
-     * the base pointer. For a heap array it is relative to the
-     * address of the first array element.
-     *
-     * This method will return the result of doing an elementSize byte
-     * read starting at offset (in bytes).
-     *
-     * @param offset offset into buffer
-     * @param elemSize size (in bytes) of the element
-     *
-     * @return the verification data, only the least significant
-     * elemSize*8 bits are set, zero extended
-     */
-    private long getVerificationDataForOffset(long offset, long elemSize) {
-        byte[] bytes = new byte[(int)elemSize];
-
-        for (long i = 0; i < elemSize; i++) {
-            bytes[(int)i] = (byte)(offset + i);
-        }
-
-        long o = UNSAFE.arrayBaseOffset(byte[].class);
-
-        switch ((int)elemSize) {
-        case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o));
-        case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o));
-        case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o));
-        case 8: return UNSAFE.getLongUnaligned(bytes, o);
-        default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
-        }
-    }
-
-    /**
-     * Verify byte swapped data
-     *
-     * @param ptr the data to verify
-     * @param srcOffset the srcOffset (in bytes) from which the copy started,
-     *        used as key to regenerate the verification data
-     * @param dstOffset the offset (in bytes) in the array at which to start
-     *        the verification, relative to the first element in the array
-     * @param size size (in bytes) of data to to verify
-     * @param elemSize size (in bytes) of the individual array elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) {
-        for (long offset = 0; offset < size; offset += elemSize) {
-            long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize);
-            long expected = byteSwap(expectedUnswapped, elemSize);
-
-            long actual = getArrayElem(ptr, dstOffset + offset, elemSize);
-
-            if (expected != actual) {
-                throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) +
-                                           " dstOffset: 0x" + Long.toHexString(dstOffset) +
-                                           " size: 0x" + Long.toHexString(size) +
-                                           " offset: 0x" + Long.toHexString(offset) +
-                                           " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) +
-                                           " expected: 0x" + Long.toHexString(expected) +
-                                           " != actual: 0x" + Long.toHexString(actual));
-            }
-        }
-    }
-
-    /**
-     * Initialize an array with verification friendly data
-     *
-     * @param ptr pointer to the data to initialize
-     * @param size size (in bytes) of the data
-     * @param elemSize size (in bytes) of the individual elements
-     */
-    private void initVerificationData(GenericPointer ptr, long size, long elemSize) {
-        for (long offset = 0; offset < size; offset++) {
-            byte data = (byte)getVerificationDataForOffset(offset, 1);
-
-            if (ptr.isOnHeap()) {
-                UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data);
-            } else {
-                UNSAFE.putByte(ptr.getOffset() + offset, data);
-            }
-        }
-    }
-
-    /**
-     * Allocate a primitive array
-     *
-     * @param size size (in bytes) of all the array elements (elemSize * length)
-     * @param elemSize the size of the array elements
-     *
-     * @return a newly allocated primitive array
-     */
-    Object allocArray(long size, long elemSize) {
-        int length = (int)(size / elemSize);
-
-        switch ((int)elemSize) {
-        case 1: return new byte[length];
-        case 2: return new short[length];
-        case 4: return new int[length];
-        case 8: return new long[length];
-        default:
-            throw new IllegalArgumentException("Invalid element size: " + elemSize);
-        }
-    }
-
-    /**
-     * Get the value of a primitive array entry
-     *
-     * @param ptr pointer to the data
-     * @param offset offset (in bytes) of the array element, relative to the first element in the array
-     *
-     * @return the array element, as an unsigned long
-     */
-    private long getArrayElem(GenericPointer ptr, long offset, long elemSize) {
-        if (ptr.isOnHeap()) {
-            Object o = ptr.getObject();
-            int index = (int)(offset / elemSize);
-
-            if (o instanceof short[]) {
-                short[] arr = (short[])o;
-                return Short.toUnsignedLong(arr[index]);
-            } else if (o instanceof int[]) {
-                int[] arr = (int[])o;
-                return Integer.toUnsignedLong(arr[index]);
-            } else if (o instanceof long[]) {
-                long[] arr = (long[])o;
-                return arr[index];
-            } else {
-                throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
-            }
-        } else {
-            long addr = ptr.getOffset() + offset;
-
-            switch ((int)elemSize) {
-            case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr));
-            case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr));
-            case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr));
-            case 8: return UNSAFE.getLongUnaligned(null, addr);
-            default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
-            }
-        }
-    }
-
-    private void putValue(long addr, long elemSize, long value) {
-        switch ((int)elemSize) {
-        case 1: UNSAFE.putByte(addr, (byte)value); break;
-        case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break;
-        case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break;
-        case 8: UNSAFE.putLongUnaligned(null, addr, value); break;
-        default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
-        }
-    }
-
-    /**
-     * Get the size of the elements for an array
-     *
-     * @param o a primitive heap array
-     *
-     * @return the size (in bytes) of the individual array elements
-     */
-    private long getArrayElemSize(Object o) {
-        if (o instanceof short[]) {
-            return 2;
-        } else if (o instanceof int[]) {
-            return 4;
-        } else if (o instanceof long[]) {
-            return 8;
-        } else {
-            throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
-        }
-    }
-
-    /**
-     * Byte swap a value
-     *
-     * @param value the value to swap, only the bytes*8 least significant bits are used
-     * @param size size (in bytes) of the value
-     *
-     * @return the byte swapped value in the bytes*8 least significant bits
-     */
-    private long byteSwap(long value, long size) {
-        switch ((int)size) {
-        case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value));
-        case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value));
-        case 8: return Long.reverseBytes(value);
-        default: throw new IllegalArgumentException("Invalid element size: " + size);
-        }
-    }
-
-    /**
-     * Verify data which has *not* been byte swapped
-     *
-     * @param ptr the data to verify
-     * @param startOffset the offset (in bytes) at which to start the verification
-     * @param size size (in bytes) of the data to verify
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void verifyUnswappedData(GenericPointer ptr, long startOffset, long srcOffset, long size) {
-        for (long i = 0; i <  size; i++) {
-            byte expected = (byte)getVerificationDataForOffset(srcOffset + i, 1);
-
-            byte actual;
-            if (ptr.isOnHeap()) {
-                actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + startOffset + i);
-            } else {
-                actual = UNSAFE.getByte(ptr.getOffset() + startOffset + i);
-            }
-
-            if (expected != actual) {
-                throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) +
-                                           " srcOffset: 0x" + Long.toHexString(srcOffset) +
-                                           " size: 0x" + Long.toHexString(size) +
-                                           " i: 0x" + Long.toHexString(i) +
-                                           " expected: 0x" + Long.toHexString(expected) +
-                                           " != actual: 0x" + Long.toHexString(actual));
-            }
-        }
-    }
-
-
-    /**
-     * Copy and byte swap data from the source to the destination
-     *
-     * This method will pre-populate the whole source and destination
-     * buffers with verification friendly data. It will then use
-     * copypMemory to fill part of the destination buffer with
-     * data from the source. Some space (padding) will be
-     * left before and after the data in the destination buffer, which
-     * should not be touched/overwritten by the copy call.
-     *
-     * Note: Both source and destination buffers will be overwritten!
-     *
-     * @param src source buffer to copy from
-     * @param srcOffset the offset (in bytes) in the source buffer, relative to
-     *        the first array element, at which to start reading data
-     * @param dst destination buffer to copy to
-     * @param dstOffset the offset (in bytes) in the destination
-     *        buffer, relative to the first array element, at which to
-     *        start writing data
-     * @param bufSize the size (in bytes) of the src and dst arrays
-     * @param copyBytes the size (in bytes) of the copy to perform,
-     *        must be a multiple of elemSize
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void testCopy(GenericPointer src, long srcOffset,
-                          GenericPointer dst, long dstOffset,
-                          long bufSize, long copyBytes) {
-        if (srcOffset + copyBytes > bufSize) {
-            throw new IllegalArgumentException(
-                "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
-        }
-        if (dstOffset + copyBytes > bufSize) {
-            throw new IllegalArgumentException(
-                "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
-        }
-
-        // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes)
-        initVerificationData(src, bufSize, 1);
-        if (!src.equals(dst)) {
-            initVerificationData(dst, bufSize, 1);
-        }
-
-        if (DEBUG) {
-            System.out.println("===before===");
-            for (int offset = 0; offset < bufSize; offset++) {
-                long srcValue = getArrayElem(src, offset, 1);
-                long dstValue = getArrayElem(dst, offset, 1);
-
-                System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
-                                 " src=0x" + Long.toHexString(srcValue) +
-                                 " dst=0x" + Long.toHexString(dstValue));
-            }
-        }
-
-        // Copy & swap data into the middle of the destination buffer
-        UNSAFE.copyMemory(src.getObject(),
-                          src.getOffset() + srcOffset,
-                          dst.getObject(),
-                          dst.getOffset() + dstOffset,
-                          copyBytes);
-
-        if (DEBUG) {
-            System.out.println("===after===");
-            for (int offset = 0; offset < bufSize; offset++) {
-                long srcValue = getArrayElem(src, offset, 1);
-                long dstValue = getArrayElem(dst, offset, 1);
-
-                System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
-                                 " src=0x" + Long.toHexString(srcValue) +
-                                 " dst=0x" + Long.toHexString(dstValue));
-            }
-        }
-
-        // Verify the the front padding is unchanged
-        verifyUnswappedData(dst, 0, 0, dstOffset);
-
-        // Verify copied data
-        verifyUnswappedData(dst, dstOffset, srcOffset, copyBytes);
-
-        // Verify that the back back padding is unchanged
-        long frontAndDataBytes = dstOffset + copyBytes;
-        long trailingBytes = bufSize - frontAndDataBytes;
-        verifyUnswappedData(dst, frontAndDataBytes, frontAndDataBytes, trailingBytes);
-    }
-
-    /**
-     * Test various configurations copying from one buffer to the other
-     *
-     * @param src the source buffer to copy from
-     * @param dst the destination buffer to copy to
-     * @param size size (in bytes) of the buffers
-     * @param elemSize size (in bytes) of the individual elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize) {
-        // offset in source from which to start reading data
-        for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) {
-
-            // offset in destination at which to start writing data
-            for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) {
-
-                // number of bytes to copy
-                long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset);
-                for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) {
-                    try {
-                        testCopy(src, srcOffset, dst, dstOffset, size, copyBytes);
-                    } catch (RuntimeException e) {
-                        // Wrap the exception in another exception to catch the relevant configuration data
-                        throw new RuntimeException("testBufferPair: " +
-                                                   "src=" + src +
-                                                   " dst=" + dst +
-                                                   " elemSize=0x" + Long.toHexString(elemSize) +
-                                                   " copyBytes=0x" + Long.toHexString(copyBytes) +
-                                                   " srcOffset=0x" + Long.toHexString(srcOffset) +
-                                                   " dstOffset=0x" + Long.toHexString(dstOffset),
-                                                   e);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Test copying between various permutations of buffers
-     *
-     * @param buffers buffers to permute (src x dst)
-     * @param size size (in bytes) of buffers
-     * @param elemSize size (in bytes) of individual elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize) {
-        for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) {
-            for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) {
-                testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize);
-            }
-        }
-    }
-
-    /**
-     * Test copying of a specific element size
-     *
-     * @param size size (in bytes) of buffers to allocate
-     * @param elemSize size (in bytes) of individual elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void testElemSize(long size, long elemSize) {
-        long buf1Raw = 0;
-        long buf2Raw = 0;
-
-        try {
-            buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
-            long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT);
-
-            buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
-            long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT);
-
-            GenericPointer[] buffers = {
-                new GenericPointer(buf1),
-                new GenericPointer(buf2),
-                new GenericPointer(allocArray(size, elemSize)),
-                new GenericPointer(allocArray(size, elemSize))
-            };
-
-            testPermuteBuffers(buffers, size, elemSize);
-        } finally {
-            if (buf1Raw != 0) {
-                UNSAFE.freeMemory(buf1Raw);
-            }
-            if (buf2Raw != 0) {
-                UNSAFE.freeMemory(buf2Raw);
-            }
-        }
-    }
-
-    /**
-     * Verify that small copies work
-     */
-    private void testSmallCopy() {
-        int smallBufSize = SMALL_COPY_SIZE;
-
-        testElemSize(smallBufSize, 1);
-    }
-
-
-    /**
-     * Verify that large copies work
-     */
-    private void testLargeCopy() {
-        long size = 2 * GB + 8;
-        long bufRaw = 0;
-
-        // Check that a large native copy succeeds
-        try {
-            try {
-                bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
-            } catch (OutOfMemoryError e) {
-                // Accept failure, skip test
-                return;
-            }
-
-            long buf = alignUp(bufRaw, BASE_ALIGNMENT);
-
-            UNSAFE.copyMemory(null, buf, null, buf, size);
-        } catch (Exception e) {
-            throw new RuntimeException("copyMemory of large buffer failed");
-        } finally {
-            if (bufRaw != 0) {
-                UNSAFE.freeMemory(bufRaw);
-            }
-        }
-    }
-
-    /**
      * Run positive tests
      *
      * @throws RuntimeException if an error is found
      */
     private void testPositive() {
-        testSmallCopy();
-        testLargeCopy();
+        testSmallCopy(false);
+        testLargeCopy(false);
     }
 
     /**
@@ -527,7 +53,7 @@
 
         try {
             bufRaw = UNSAFE.allocateMemory(1024);
-            long buf = alignUp(bufRaw, BASE_ALIGNMENT);
+            long buf = CopyCommon.alignUp(bufRaw, CopyCommon.BASE_ALIGNMENT);
             short[] arr = new short[16];
 
             // Check illegal sizes
@@ -609,59 +135,4 @@
         CopyMemory cs = new CopyMemory();
         cs.test();
     }
-
-    /**
-     * Helper class to represent a "pointer" - either a heap array or
-     * a pointer to a native buffer.
-     *
-     * In the case of a native pointer, the Object is null and the offset is
-     * the absolute address of the native buffer.
-     *
-     * In the case of a heap object, the Object is a primitive array, and
-     * the offset will be set to the base offset to the first element, meaning
-     * the object and the offset together form a double-register pointer.
-     */
-    static class GenericPointer {
-        private final Object o;
-        private final long offset;
-
-        private GenericPointer(Object o, long offset) {
-            this.o = o;
-            this.offset = offset;
-        }
-
-        public String toString() {
-            return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")";
-        }
-
-        public boolean equals(Object other) {
-            if (!(other instanceof GenericPointer)) {
-                return false;
-            }
-
-            GenericPointer otherp = (GenericPointer)other;
-
-            return o == otherp.o && offset == otherp.offset;
-        }
-
-        GenericPointer(Object o) {
-            this(o, UNSAFE.arrayBaseOffset(o.getClass()));
-        }
-
-        GenericPointer(long offset) {
-            this(null, offset);
-        }
-
-        public boolean isOnHeap() {
-            return o != null;
-        }
-
-        public Object getObject() {
-            return o;
-        }
-
-        public long getOffset() {
-            return offset;
-        }
-    }
 }
--- a/test/jdk/internal/misc/Unsafe/CopySwap.java	Wed Mar 16 13:16:14 2016 -0400
+++ b/test/jdk/internal/misc/Unsafe/CopySwap.java	Wed Mar 16 14:04:12 2016 -0700
@@ -29,507 +29,18 @@
  * @summary Test Unsafe.copySwapMemory
  * @modules java.base/jdk.internal.misc
  */
-public class CopySwap {
-    private static final boolean DEBUG = Boolean.getBoolean("CopySwap.DEBUG");
-
-    public static final long KB = 1024;
-    public static final long MB = KB * 1024;
-    public static final long GB = MB * 1024;
-
-    private static final Unsafe UNSAFE;
-    private static final int SMALL_COPY_SIZE = 32;
-    private static final int BASE_ALIGNMENT = 16;
-
-    static {
-        try {
-            Field f = jdk.internal.misc.Unsafe.class.getDeclaredField("theUnsafe");
-            f.setAccessible(true);
-            UNSAFE = (jdk.internal.misc.Unsafe) f.get(null);
-        } catch (Exception e) {
-            throw new RuntimeException("Unable to get Unsafe instance.", e);
-        }
-    }
-
-    private static long alignDown(long value, long alignment) {
-        return value & ~(alignment - 1);
-    }
-
-    private static long alignUp(long value, long alignment) {
-        return (value + alignment - 1) & ~(alignment - 1);
-    }
-
-    private static boolean isAligned(long value, long alignment) {
-        return value == alignDown(value, alignment);
-    }
-
+public class CopySwap extends CopyCommon {
     private CopySwap() {
     }
 
     /**
-     * Generate verification data for a given offset
-     *
-     * The verification data is used to verify that the correct bytes
-     * have indeed been copied and byte swapped.
-     *
-     * The data is generated based on the offset (in bytes) into the
-     * source buffer. For a native buffer the offset is relative to
-     * the base pointer. For a heap array it is relative to the
-     * address of the first array element.
-     *
-     * This method will return the result of doing an elementSize byte
-     * read starting at offset (in bytes).
-     *
-     * @param offset offset into buffer
-     * @param elemSize size (in bytes) of the element
-     *
-     * @return the verification data, only the least significant
-     * elemSize*8 bits are set, zero extended
-     */
-    private long getVerificationDataForOffset(long offset, long elemSize) {
-        byte[] bytes = new byte[(int)elemSize];
-
-        for (long i = 0; i < elemSize; i++) {
-            bytes[(int)i] = (byte)(offset + i);
-        }
-
-        long o = UNSAFE.arrayBaseOffset(byte[].class);
-
-        switch ((int)elemSize) {
-        case 1: return Byte.toUnsignedLong(UNSAFE.getByte(bytes, o));
-        case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o));
-        case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o));
-        case 8: return UNSAFE.getLongUnaligned(bytes, o);
-        default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
-        }
-    }
-
-    /**
-     * Verify byte swapped data
-     *
-     * @param ptr the data to verify
-     * @param srcOffset the srcOffset (in bytes) from which the copy started,
-     *        used as key to regenerate the verification data
-     * @param dstOffset the offset (in bytes) in the array at which to start
-     *        the verification, relative to the first element in the array
-     * @param size size (in bytes) of data to to verify
-     * @param elemSize size (in bytes) of the individual array elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void verifySwappedData(GenericPointer ptr, long srcOffset, long dstOffset, long size, long elemSize) {
-        for (long offset = 0; offset < size; offset += elemSize) {
-            long expectedUnswapped = getVerificationDataForOffset(srcOffset + offset, elemSize);
-            long expected = byteSwap(expectedUnswapped, elemSize);
-
-            long actual = getArrayElem(ptr, dstOffset + offset, elemSize);
-
-            if (expected != actual) {
-                throw new RuntimeException("srcOffset: 0x" + Long.toHexString(srcOffset) +
-                                           " dstOffset: 0x" + Long.toHexString(dstOffset) +
-                                           " size: 0x" + Long.toHexString(size) +
-                                           " offset: 0x" + Long.toHexString(offset) +
-                                           " expectedUnswapped: 0x" + Long.toHexString(expectedUnswapped) +
-                                           " expected: 0x" + Long.toHexString(expected) +
-                                           " != actual: 0x" + Long.toHexString(actual));
-            }
-        }
-    }
-
-    /**
-     * Initialize an array with verification friendly data
-     *
-     * @param ptr pointer to the data to initialize
-     * @param size size (in bytes) of the data
-     * @param elemSize size (in bytes) of the individual elements
-     */
-    private void initVerificationData(GenericPointer ptr, long size, long elemSize) {
-        for (long offset = 0; offset < size; offset++) {
-            byte data = (byte)getVerificationDataForOffset(offset, 1);
-
-            if (ptr.isOnHeap()) {
-                UNSAFE.putByte(ptr.getObject(), ptr.getOffset() + offset, data);
-            } else {
-                UNSAFE.putByte(ptr.getOffset() + offset, data);
-            }
-        }
-    }
-
-    /**
-     * Allocate a primitive array
-     *
-     * @param size size (in bytes) of all the array elements (elemSize * length)
-     * @param elemSize the size of the array elements
-     *
-     * @return a newly allocated primitive array
-     */
-    Object allocArray(long size, long elemSize) {
-        int length = (int)(size / elemSize);
-
-        switch ((int)elemSize) {
-        case 2: return new short[length];
-        case 4: return new int[length];
-        case 8: return new long[length];
-        default:
-            throw new IllegalArgumentException("Invalid element size: " + elemSize);
-        }
-    }
-
-    /**
-     * Get the value of a primitive array entry
-     *
-     * @param ptr pointer to the data
-     * @param offset offset (in bytes) of the array element, relative to the first element in the array
-     *
-     * @return the array element, as an unsigned long
-     */
-    private long getArrayElem(GenericPointer ptr, long offset, long elemSize) {
-        if (ptr.isOnHeap()) {
-            Object o = ptr.getObject();
-            int index = (int)(offset / elemSize);
-
-            if (o instanceof short[]) {
-                short[] arr = (short[])o;
-                return Short.toUnsignedLong(arr[index]);
-            } else if (o instanceof int[]) {
-                int[] arr = (int[])o;
-                return Integer.toUnsignedLong(arr[index]);
-            } else if (o instanceof long[]) {
-                long[] arr = (long[])o;
-                return arr[index];
-            } else {
-                throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
-            }
-        } else {
-            long addr = ptr.getOffset() + offset;
-
-            switch ((int)elemSize) {
-            case 1: return Byte.toUnsignedLong(UNSAFE.getByte(addr));
-            case 2: return Short.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr));
-            case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr));
-            case 8: return UNSAFE.getLongUnaligned(null, addr);
-            default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
-            }
-        }
-    }
-
-    private void putValue(long addr, long elemSize, long value) {
-        switch ((int)elemSize) {
-        case 1: UNSAFE.putByte(addr, (byte)value); break;
-        case 2: UNSAFE.putShortUnaligned(null, addr, (short)value); break;
-        case 4: UNSAFE.putIntUnaligned(null, addr, (int)value); break;
-        case 8: UNSAFE.putLongUnaligned(null, addr, value); break;
-        default: throw new IllegalArgumentException("Invalid element size: " + elemSize);
-        }
-    }
-
-    /**
-     * Get the size of the elements for an array
-     *
-     * @param o a primitive heap array
-     *
-     * @return the size (in bytes) of the individual array elements
-     */
-    private long getArrayElemSize(Object o) {
-        if (o instanceof short[]) {
-            return 2;
-        } else if (o instanceof int[]) {
-            return 4;
-        } else if (o instanceof long[]) {
-            return 8;
-        } else {
-            throw new IllegalArgumentException("Invalid object type: " + o.getClass().getName());
-        }
-    }
-
-    /**
-     * Byte swap a value
-     *
-     * @param value the value to swap, only the bytes*8 least significant bits are used
-     * @param size size (in bytes) of the value
-     *
-     * @return the byte swapped value in the bytes*8 least significant bits
-     */
-    private long byteSwap(long value, long size) {
-        switch ((int)size) {
-        case 2: return Short.toUnsignedLong(Short.reverseBytes((short)value));
-        case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value));
-        case 8: return Long.reverseBytes(value);
-        default: throw new IllegalArgumentException("Invalid element size: " + size);
-        }
-    }
-
-    /**
-     * Verify data in a heap array which has *not* been byte swapped
-     *
-     * @param ptr the data to verify
-     * @param startOffset the offset (in bytes) at which to start the verification
-     * @param size size (in bytes) of the data to verify
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void verifyUnswappedData(GenericPointer ptr, long startOffset, long size) {
-        for (long elemOffset = startOffset; elemOffset < startOffset + size; elemOffset++) {
-            byte expected = (byte)getVerificationDataForOffset(elemOffset, 1);
-
-            byte actual;
-            if (ptr.isOnHeap()) {
-                actual = UNSAFE.getByte(ptr.getObject(), ptr.getOffset() + elemOffset);
-            } else {
-                actual = UNSAFE.getByte(ptr.getOffset() + elemOffset);
-            }
-
-            if (expected != actual) {
-                throw new RuntimeException("startOffset: 0x" + Long.toHexString(startOffset) +
-                                           " size: 0x" + Long.toHexString(size) +
-                                           " elemOffset: 0x" + Long.toHexString(elemOffset) +
-                                           " expected: 0x" + Long.toHexString(expected) +
-                                           " != actual: 0x" + Long.toHexString(actual));
-            }
-        }
-    }
-
-
-    /**
-     * Copy and byte swap data from the source to the destination
-     *
-     * This method will pre-populate the whole source and destination
-     * buffers with verification friendly data. It will then use
-     * copySwapMemory to fill part of the destination buffer with
-     * swapped data from the source. Some space (padding) will be
-     * left before and after the data in the destination buffer, which
-     * should not be touched/overwritten by the copy call.
-     *
-     * Note: Both source and destination buffers will be overwritten!
-     *
-     * @param src source buffer to copy from
-     * @param srcOffset the offset (in bytes) in the source buffer, relative to
-     *        the first array element, at which to start reading data
-     * @param dst destination buffer to copy to
-     * @param dstOffset the offset (in bytes) in the destination
-     *        buffer, relative to the first array element, at which to
-     *        start writing data
-     * @param bufSize the size (in bytes) of the src and dst arrays
-     * @param copyBytes the size (in bytes) of the copy to perform,
-     *        must be a multiple of elemSize
-     * @param elemSize the size (in bytes) of the elements to byte swap
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void testCopySwap(GenericPointer src, long srcOffset,
-                              GenericPointer dst, long dstOffset,
-                              long bufSize, long copyBytes, long elemSize) {
-        if (!isAligned(copyBytes, elemSize)) {
-            throw new IllegalArgumentException(
-                "copyBytes (" + copyBytes + ") must be a multiple of elemSize (" + elemSize + ")");
-        }
-        if (src.isOnHeap() && !isAligned(srcOffset, elemSize)) {
-            throw new IllegalArgumentException(
-                "srcOffset (" + srcOffset + ") must be a multiple of elemSize (" + elemSize + ")");
-        }
-        if (dst.isOnHeap() && !isAligned(dstOffset, elemSize)) {
-            throw new IllegalArgumentException(
-                "dstOffset (" + dstOffset + ") must be a multiple of elemSize (" + elemSize + ")");
-        }
-        if (srcOffset + copyBytes > bufSize) {
-            throw new IllegalArgumentException(
-                "srcOffset (" + srcOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
-        }
-        if (dstOffset + copyBytes > bufSize) {
-            throw new IllegalArgumentException(
-                "dstOffset (" + dstOffset + ") + copyBytes (" + copyBytes + ") > bufSize (" + bufSize + ")");
-        }
-
-        // Initialize the whole source buffer with a verification friendly pattern (no 0x00 bytes)
-        initVerificationData(src, bufSize, elemSize);
-        if (!src.equals(dst)) {
-            initVerificationData(dst, bufSize, elemSize);
-        }
-
-        if (DEBUG) {
-            System.out.println("===before===");
-            for (int offset = 0; offset < bufSize; offset += elemSize) {
-                long srcValue = getArrayElem(src, offset, elemSize);
-                long dstValue = getArrayElem(dst, offset, elemSize);
-
-                System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
-                                 " src=0x" + Long.toHexString(srcValue) +
-                                 " dst=0x" + Long.toHexString(dstValue));
-            }
-        }
-
-        // Copy & swap data into the middle of the destination buffer
-        UNSAFE.copySwapMemory(src.getObject(),
-                              src.getOffset() + srcOffset,
-                              dst.getObject(),
-                              dst.getOffset() + dstOffset,
-                              copyBytes,
-                              elemSize);
-
-        if (DEBUG) {
-            System.out.println("===after===");
-            for (int offset = 0; offset < bufSize; offset += elemSize) {
-                long srcValue = getArrayElem(src, offset, elemSize);
-                long dstValue = getArrayElem(dst, offset, elemSize);
-
-                System.out.println("offs=0x" + Long.toHexString(Integer.toUnsignedLong(offset)) +
-                                 " src=0x" + Long.toHexString(srcValue) +
-                                 " dst=0x" + Long.toHexString(dstValue));
-            }
-        }
-
-        // Verify the the front padding is unchanged
-        verifyUnswappedData(dst, 0, dstOffset);
-
-        // Verify swapped data
-        verifySwappedData(dst, srcOffset, dstOffset, copyBytes, elemSize);
-
-        // Verify that the back back padding is unchanged
-        long frontAndDataBytes = dstOffset + copyBytes;
-        long trailingBytes = bufSize - frontAndDataBytes;
-        verifyUnswappedData(dst, frontAndDataBytes, trailingBytes);
-    }
-
-    /**
-     * Test various configurations copy-swapping from one buffer to the other
-     *
-     * @param src the source buffer to copy from
-     * @param dst the destination buffer to copy to
-     * @param size size (in bytes) of the buffers
-     * @param elemSize size (in bytes) of the individual elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    public void testBufferPair(GenericPointer src, GenericPointer dst, long size, long elemSize) {
-        // offset in source from which to start reading data
-        for (long srcOffset = 0; srcOffset < size; srcOffset += (src.isOnHeap() ? elemSize : 1)) {
-
-            // offset in destination at which to start writing data
-            for (int dstOffset = 0; dstOffset < size; dstOffset += (dst.isOnHeap() ? elemSize : 1)) {
-
-                // number of bytes to copy
-                long maxCopyBytes = Math.min(size - srcOffset, size - dstOffset);
-                for (long copyBytes = 0; copyBytes < maxCopyBytes; copyBytes += elemSize) {
-                    try {
-                        testCopySwap(src, srcOffset, dst, dstOffset, size, copyBytes, elemSize);
-                    } catch (RuntimeException e) {
-                        // Wrap the exception in another exception to catch the relevant configuration data
-                        throw new RuntimeException("testBufferPair: " +
-                                                   "src=" + src +
-                                                   " dst=" + dst +
-                                                   " elemSize=0x" + Long.toHexString(elemSize) +
-                                                   " copyBytes=0x" + Long.toHexString(copyBytes) +
-                                                   " srcOffset=0x" + Long.toHexString(srcOffset) +
-                                                   " dstOffset=0x" + Long.toHexString(dstOffset),
-                                                   e);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Test copying between various permutations of buffers
-     *
-     * @param buffers buffers to permute (src x dst)
-     * @param size size (in bytes) of buffers
-     * @param elemSize size (in bytes) of individual elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    public void testPermuteBuffers(GenericPointer[] buffers, long size, long elemSize) {
-        for (int srcIndex = 0; srcIndex < buffers.length; srcIndex++) {
-            for (int dstIndex = 0; dstIndex < buffers.length; dstIndex++) {
-                testBufferPair(buffers[srcIndex], buffers[dstIndex], size, elemSize);
-            }
-        }
-    }
-
-    /**
-     * Test copying of a specific element size
-     *
-     * @param size size (in bytes) of buffers to allocate
-     * @param elemSize size (in bytes) of individual elements
-     *
-     * @throws RuntimeException if an error is found
-     */
-    private void testElemSize(long size, long elemSize) {
-        long buf1Raw = 0;
-        long buf2Raw = 0;
-
-        try {
-            buf1Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
-            long buf1 = alignUp(buf1Raw, BASE_ALIGNMENT);
-
-            buf2Raw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
-            long buf2 = alignUp(buf2Raw, BASE_ALIGNMENT);
-
-            GenericPointer[] buffers = {
-                new GenericPointer(buf1),
-                new GenericPointer(buf2),
-                new GenericPointer(allocArray(size, elemSize)),
-                new GenericPointer(allocArray(size, elemSize))
-            };
-
-            testPermuteBuffers(buffers, size, elemSize);
-        } finally {
-            if (buf1Raw != 0) {
-                UNSAFE.freeMemory(buf1Raw);
-            }
-            if (buf2Raw != 0) {
-                UNSAFE.freeMemory(buf2Raw);
-            }
-        }
-    }
-
-    /**
-     * Verify that small copy swaps work
-     */
-    private void testSmallCopy() {
-        int smallBufSize = SMALL_COPY_SIZE;
-
-        // Test various element types and heap/native combinations
-        for (long elemSize = 2; elemSize <= 8; elemSize <<= 1) {
-            testElemSize(smallBufSize, elemSize);
-        }
-    }
-
-
-    /**
-     * Verify that large copy swaps work
-     */
-    private void testLargeCopy() {
-        long size = 2 * GB + 8;
-        long bufRaw = 0;
-
-        // Check that a large native copy succeeds
-        try {
-            try {
-                bufRaw = UNSAFE.allocateMemory(size + BASE_ALIGNMENT);
-            } catch (OutOfMemoryError e) {
-                // Accept failure, skip test
-                return;
-            }
-
-            long buf = alignUp(bufRaw, BASE_ALIGNMENT);
-
-            UNSAFE.copySwapMemory(null, buf, null, buf, size, 8);
-        } catch (Exception e) {
-            throw new RuntimeException("copySwapMemory of large buffer failed");
-        } finally {
-            if (bufRaw != 0) {
-                UNSAFE.freeMemory(bufRaw);
-            }
-        }
-    }
-
-    /**
      * Run positive tests
      *
      * @throws RuntimeException if an error is found
      */
     private void testPositive() {
-        testSmallCopy();
-        testLargeCopy();
+        testSmallCopy(true);
+        testLargeCopy(true);
     }
 
     /**
@@ -542,7 +53,7 @@
 
         try {
             bufRaw = UNSAFE.allocateMemory(1024);
-            long buf = alignUp(bufRaw, BASE_ALIGNMENT);
+            long buf = CopyCommon.alignUp(bufRaw, CopyCommon.BASE_ALIGNMENT);
             short[] arr = new short[16];
 
             // Check various illegal element sizes
@@ -637,59 +148,4 @@
         CopySwap cs = new CopySwap();
         cs.test();
     }
-
-    /**
-     * Helper class to represent a "pointer" - either a heap array or
-     * a pointer to a native buffer.
-     *
-     * In the case of a native pointer, the Object is null and the offset is
-     * the absolute address of the native buffer.
-     *
-     * In the case of a heap object, the Object is a primitive array, and
-     * the offset will be set to the base offset to the first element, meaning
-     * the object and the offset together form a double-register pointer.
-     */
-    static class GenericPointer {
-        private final Object o;
-        private final long offset;
-
-        private GenericPointer(Object o, long offset) {
-            this.o = o;
-            this.offset = offset;
-        }
-
-        public String toString() {
-            return "GenericPointer(o={" + o + "}, offset=0x" + Long.toHexString(offset) + ")";
-        }
-
-        public boolean equals(Object other) {
-            if (!(other instanceof GenericPointer)) {
-                return false;
-            }
-
-            GenericPointer otherp = (GenericPointer)other;
-
-            return o == otherp.o && offset == otherp.offset;
-        }
-
-        GenericPointer(Object o) {
-            this(o, UNSAFE.arrayBaseOffset(o.getClass()));
-        }
-
-        GenericPointer(long offset) {
-            this(null, offset);
-        }
-
-        public boolean isOnHeap() {
-            return o != null;
-        }
-
-        public Object getObject() {
-            return o;
-        }
-
-        public long getOffset() {
-            return offset;
-        }
-    }
 }