/* * Copyright (c) 2016, 2022, 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;
/** * Helper class to support testing of Unsafe.copyMemory and Unsafe.copySwapMemory
*/ publicclass CopyCommon { privatestaticfinalboolean DEBUG = Boolean.getBoolean("CopyCommon.DEBUG");
staticboolean 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
*/ privatelong getVerificationDataForOffset(long offset, long elemSize) { byte[] bytes = newbyte[(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: returnByte.toUnsignedLong(UNSAFE.getByte(bytes, o)); case 2: returnShort.toUnsignedLong(UNSAFE.getShortUnaligned(bytes, o)); case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(bytes, o)); case 8: return UNSAFE.getLongUnaligned(bytes, o); default: thrownew 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
*/ privatevoid 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);
/** * 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
*/ privatevoid initVerificationData(GenericPointer ptr, long size, long elemSize) { for (long offset = 0; offset < size; offset++) { byte data = (byte)getVerificationDataForOffset(offset, 1);
/** * 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: returnnewbyte[length]; case 2: returnnewshort[length]; case 4: returnnewint[length]; case 8: returnnewlong[length]; default: thrownew 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
*/ privatelong getArrayElem(GenericPointer ptr, long offset, long elemSize) { if (ptr.isOnHeap()) {
Object o = ptr.getObject(); int index = (int)(offset / elemSize);
switch ((int)elemSize) { case 1: returnByte.toUnsignedLong(UNSAFE.getByte(addr)); case 2: returnShort.toUnsignedLong(UNSAFE.getShortUnaligned(null, addr)); case 4: return Integer.toUnsignedLong(UNSAFE.getIntUnaligned(null, addr)); case 8: return UNSAFE.getLongUnaligned(null, addr); default: thrownew IllegalArgumentException("Invalid element size: " + elemSize);
}
}
}
privatevoid 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: thrownew 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
*/ privatelong getArrayElemSize(Object o) { if (o instanceofshort[]) { return 2;
} elseif (o instanceofint[]) { return 4;
} elseif (o instanceoflong[]) { return 8;
} else { thrownew 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
*/ privatelong byteSwap(long value, long size) { switch ((int)size) { case 2: returnShort.toUnsignedLong(Short.reverseBytes((short)value)); case 4: return Integer.toUnsignedLong(Integer.reverseBytes((int)value)); case 8: returnLong.reverseBytes(value); default: thrownew 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
*/ privatevoid 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);
}
/** * 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)) { thrownew IllegalArgumentException( "copyBytes (" + copyBytes + ") must be a multiple of elemSize (" + elemSize + ")");
} if (src.isOnHeap() && !isAligned(srcOffset, elemSize)) { thrownew IllegalArgumentException( "srcOffset (" + srcOffset + ") must be a multiple of elemSize (" + elemSize + ")");
} if (dst.isOnHeap() && !isAligned(dstOffset, elemSize)) { thrownew IllegalArgumentException( "dstOffset (" + dstOffset + ") must be a multiple of elemSize (" + elemSize + ")");
}
}
// 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);
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);
// Verify 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
*/ publicvoid 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 thrownew 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
*/ publicvoid 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
*/ privatevoid testElemSize(long size, long elemSize, boolean swap) { long buf1Raw = 0; long buf2Raw = 0;
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))
};
/** * 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;
}
/** * 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.
*/ staticclass GenericPointer { privatefinal Object o; privatefinallong 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) + ")";
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.