--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/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/jdk/test/jdk/internal/misc/Unsafe/CopyMemory.java Wed Mar 16 13:16:14 2016 -0400
+++ b/jdk/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/jdk/test/jdk/internal/misc/Unsafe/CopySwap.java Wed Mar 16 13:16:14 2016 -0400
+++ b/jdk/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;
- }
- }
}