mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-20 15:25:27 +00:00
8151233: Unify CopySwap and CopyMemory tests
Reviewed-by: dholmes
This commit is contained in:
parent
829d62738c
commit
bda07f129a
607
jdk/test/jdk/internal/misc/Unsafe/CopyCommon.java
Normal file
607
jdk/test/jdk/internal/misc/Unsafe/CopyCommon.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,492 +29,18 @@ import java.lang.reflect.Field;
|
||||
* @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 @@ public class CopyMemory {
|
||||
|
||||
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 @@ public class CopyMemory {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,507 +29,18 @@ import java.lang.reflect.Field;
|
||||
* @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 @@ public class CopySwap {
|
||||
|
||||
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 @@ public class CopySwap {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user