mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-11 16:33:42 +00:00
Merge
This commit is contained in:
commit
58fcd079ce
@ -40,6 +40,15 @@ import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
* Although the class and all methods are public, use of this class is
|
||||
* limited because only trusted code can obtain instances of it.
|
||||
*
|
||||
* <em>Note:</em> It is the resposibility of the caller to make sure
|
||||
* arguments are checked before methods of this class are
|
||||
* called. While some rudimentary checks are performed on the input,
|
||||
* the checks are best effort and when performance is an overriding
|
||||
* priority, as when methods of this class are optimized by the
|
||||
* runtime compiler, some or all checks (if any) may be elided. Hence,
|
||||
* the caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @author John R. Rose
|
||||
* @see #getUnsafe
|
||||
*/
|
||||
@ -358,6 +367,169 @@ public final class Unsafe {
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void putAddress(long address, long x);
|
||||
|
||||
|
||||
|
||||
/// helper methods for validating various types of objects/values
|
||||
|
||||
/**
|
||||
* Create an exception reflecting that some of the input was invalid
|
||||
*
|
||||
* <em>Note:</em> It is the resposibility of the caller to make
|
||||
* sure arguments are checked before the methods are called. While
|
||||
* some rudimentary checks are performed on the input, the checks
|
||||
* are best effort and when performance is an overriding priority,
|
||||
* as when methods of this class are optimized by the runtime
|
||||
* compiler, some or all checks (if any) may be elided. Hence, the
|
||||
* caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @return an exception object
|
||||
*/
|
||||
private RuntimeException invalidInput() {
|
||||
return new IllegalArgumentException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is 32-bit clean (32 MSB are all zero)
|
||||
*
|
||||
* @param value the 64-bit value to check
|
||||
*
|
||||
* @return true if the value is 32-bit clean
|
||||
*/
|
||||
private boolean is32BitClean(long value) {
|
||||
return value >>> 32 == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the validity of a size (the equivalent of a size_t)
|
||||
*
|
||||
* @throws RuntimeException if the size is invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void checkSize(long size) {
|
||||
if (ADDRESS_SIZE == 4) {
|
||||
// Note: this will also check for negative sizes
|
||||
if (!is32BitClean(size)) {
|
||||
throw invalidInput();
|
||||
}
|
||||
} else if (size < 0) {
|
||||
throw invalidInput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the validity of a native address (the equivalent of void*)
|
||||
*
|
||||
* @throws RuntimeException if the address is invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void checkNativeAddress(long address) {
|
||||
if (ADDRESS_SIZE == 4) {
|
||||
// Accept both zero and sign extended pointers. A valid
|
||||
// pointer will, after the +1 below, either have produced
|
||||
// the value 0x0 or 0x1. Masking off the low bit allows
|
||||
// for testing against 0.
|
||||
if ((((address >> 32) + 1) & ~1) != 0) {
|
||||
throw invalidInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the validity of an offset, relative to a base object
|
||||
*
|
||||
* @param o the base object
|
||||
* @param offset the offset to check
|
||||
*
|
||||
* @throws RuntimeException if the size is invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void checkOffset(Object o, long offset) {
|
||||
if (ADDRESS_SIZE == 4) {
|
||||
// Note: this will also check for negative offsets
|
||||
if (!is32BitClean(offset)) {
|
||||
throw invalidInput();
|
||||
}
|
||||
} else if (offset < 0) {
|
||||
throw invalidInput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the validity of a double-register pointer
|
||||
*
|
||||
* Note: This code deliberately does *not* check for NPE for (at
|
||||
* least) three reasons:
|
||||
*
|
||||
* 1) NPE is not just NULL/0 - there is a range of values all
|
||||
* resulting in an NPE, which is not trivial to check for
|
||||
*
|
||||
* 2) It is the responsibility of the callers of Unsafe methods
|
||||
* to verify the input, so throwing an exception here is not really
|
||||
* useful - passing in a NULL pointer is a critical error and the
|
||||
* must not expect an exception to be thrown anyway.
|
||||
*
|
||||
* 3) the actual operations will detect NULL pointers anyway by
|
||||
* means of traps and signals (like SIGSEGV).
|
||||
*
|
||||
* @param o Java heap object, or null
|
||||
* @param offset indication of where the variable resides in a Java heap
|
||||
* object, if any, else a memory address locating the variable
|
||||
* statically
|
||||
*
|
||||
* @throws RuntimeException if the pointer is invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void checkPointer(Object o, long offset) {
|
||||
if (o == null) {
|
||||
checkNativeAddress(offset);
|
||||
} else {
|
||||
checkOffset(o, offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a type is a primitive array type
|
||||
*
|
||||
* @param c the type to check
|
||||
*
|
||||
* @return true if the type is a primitive array type
|
||||
*/
|
||||
private void checkPrimitiveArray(Class<?> c) {
|
||||
Class<?> componentType = c.getComponentType();
|
||||
if (componentType == null || !componentType.isPrimitive()) {
|
||||
throw invalidInput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a pointer is a valid primitive array type pointer
|
||||
*
|
||||
* Note: pointers off-heap are considered to be primitive arrays
|
||||
*
|
||||
* @throws RuntimeException if the pointer is invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void checkPrimitivePointer(Object o, long offset) {
|
||||
checkPointer(o, offset);
|
||||
|
||||
if (o != null) {
|
||||
// If on heap, it it must be a primitive array
|
||||
checkPrimitiveArray(o.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// wrappers for malloc, realloc, free:
|
||||
|
||||
/**
|
||||
@ -367,7 +539,16 @@ public final class Unsafe {
|
||||
* aligned for all value types. Dispose of this memory by calling {@link
|
||||
* #freeMemory}, or resize it with {@link #reallocateMemory}.
|
||||
*
|
||||
* @throws IllegalArgumentException if the size is negative or too large
|
||||
* <em>Note:</em> It is the resposibility of the caller to make
|
||||
* sure arguments are checked before the methods are called. While
|
||||
* some rudimentary checks are performed on the input, the checks
|
||||
* are best effort and when performance is an overriding priority,
|
||||
* as when methods of this class are optimized by the runtime
|
||||
* compiler, some or all checks (if any) may be elided. Hence, the
|
||||
* caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @throws RuntimeException if the size is negative or too large
|
||||
* for the native size_t type
|
||||
*
|
||||
* @throws OutOfMemoryError if the allocation is refused by the system
|
||||
@ -375,7 +556,32 @@ public final class Unsafe {
|
||||
* @see #getByte(long)
|
||||
* @see #putByte(long, byte)
|
||||
*/
|
||||
public native long allocateMemory(long bytes);
|
||||
public long allocateMemory(long bytes) {
|
||||
allocateMemoryChecks(bytes);
|
||||
|
||||
if (bytes == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long p = allocateMemory0(bytes);
|
||||
if (p == 0) {
|
||||
throw new OutOfMemoryError();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the arguments to allocateMemory
|
||||
*
|
||||
* @throws RuntimeException if the arguments are invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void allocateMemoryChecks(long bytes) {
|
||||
checkSize(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes a new block of native memory, to the given size in bytes. The
|
||||
@ -387,14 +593,50 @@ public final class Unsafe {
|
||||
* #reallocateMemory}. The address passed to this method may be null, in
|
||||
* which case an allocation will be performed.
|
||||
*
|
||||
* @throws IllegalArgumentException if the size is negative or too large
|
||||
* <em>Note:</em> It is the resposibility of the caller to make
|
||||
* sure arguments are checked before the methods are called. While
|
||||
* some rudimentary checks are performed on the input, the checks
|
||||
* are best effort and when performance is an overriding priority,
|
||||
* as when methods of this class are optimized by the runtime
|
||||
* compiler, some or all checks (if any) may be elided. Hence, the
|
||||
* caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @throws RuntimeException if the size is negative or too large
|
||||
* for the native size_t type
|
||||
*
|
||||
* @throws OutOfMemoryError if the allocation is refused by the system
|
||||
*
|
||||
* @see #allocateMemory
|
||||
*/
|
||||
public native long reallocateMemory(long address, long bytes);
|
||||
public long reallocateMemory(long address, long bytes) {
|
||||
reallocateMemoryChecks(address, bytes);
|
||||
|
||||
if (bytes == 0) {
|
||||
freeMemory(address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long p = (address == 0) ? allocateMemory0(bytes) : reallocateMemory0(address, bytes);
|
||||
if (p == 0) {
|
||||
throw new OutOfMemoryError();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the arguments to reallocateMemory
|
||||
*
|
||||
* @throws RuntimeException if the arguments are invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void reallocateMemoryChecks(long address, long bytes) {
|
||||
checkPointer(null, address);
|
||||
checkSize(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all bytes in a given block of memory to a fixed value
|
||||
@ -411,9 +653,28 @@ public final class Unsafe {
|
||||
* If the effective address and length are (resp.) even modulo 4 or 2,
|
||||
* the stores take place in units of 'int' or 'short'.
|
||||
*
|
||||
* <em>Note:</em> It is the resposibility of the caller to make
|
||||
* sure arguments are checked before the methods are called. While
|
||||
* some rudimentary checks are performed on the input, the checks
|
||||
* are best effort and when performance is an overriding priority,
|
||||
* as when methods of this class are optimized by the runtime
|
||||
* compiler, some or all checks (if any) may be elided. Hence, the
|
||||
* caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @throws RuntimeException if any of the arguments is invalid
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public native void setMemory(Object o, long offset, long bytes, byte value);
|
||||
public void setMemory(Object o, long offset, long bytes, byte value) {
|
||||
setMemoryChecks(o, offset, bytes, value);
|
||||
|
||||
if (bytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setMemory0(o, offset, bytes, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all bytes in a given block of memory to a fixed value
|
||||
@ -426,6 +687,19 @@ public final class Unsafe {
|
||||
setMemory(null, address, bytes, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the arguments to setMemory
|
||||
*
|
||||
* @throws RuntimeException if the arguments are invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void setMemoryChecks(Object o, long offset, long bytes, byte value) {
|
||||
checkPrimitivePointer(o, offset);
|
||||
checkSize(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all bytes in a given block of memory to a copy of another
|
||||
* block.
|
||||
@ -441,12 +715,31 @@ public final class Unsafe {
|
||||
* If the effective addresses and length are (resp.) even modulo 4 or 2,
|
||||
* the transfer takes place in units of 'int' or 'short'.
|
||||
*
|
||||
* <em>Note:</em> It is the resposibility of the caller to make
|
||||
* sure arguments are checked before the methods are called. While
|
||||
* some rudimentary checks are performed on the input, the checks
|
||||
* are best effort and when performance is an overriding priority,
|
||||
* as when methods of this class are optimized by the runtime
|
||||
* compiler, some or all checks (if any) may be elided. Hence, the
|
||||
* caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @throws RuntimeException if any of the arguments is invalid
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native void copyMemory(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes);
|
||||
public void copyMemory(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes) {
|
||||
copyMemoryChecks(srcBase, srcOffset, destBase, destOffset, bytes);
|
||||
|
||||
if (bytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyMemory0(srcBase, srcOffset, destBase, destOffset, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all bytes in a given block of memory to a copy of another
|
||||
* block. This provides a <em>single-register</em> addressing mode,
|
||||
@ -458,15 +751,22 @@ public final class Unsafe {
|
||||
copyMemory(null, srcAddress, null, destAddress, bytes);
|
||||
}
|
||||
|
||||
private boolean isPrimitiveArray(Class<?> c) {
|
||||
Class<?> componentType = c.getComponentType();
|
||||
return componentType != null && componentType.isPrimitive();
|
||||
/**
|
||||
* Validate the arguments to copyMemory
|
||||
*
|
||||
* @throws RuntimeException if any of the arguments is invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void copyMemoryChecks(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes) {
|
||||
checkSize(bytes);
|
||||
checkPrimitivePointer(srcBase, srcOffset);
|
||||
checkPrimitivePointer(destBase, destOffset);
|
||||
}
|
||||
|
||||
private native void copySwapMemory0(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes, long elemSize);
|
||||
|
||||
/**
|
||||
* Copies all elements from one block of memory to another block,
|
||||
* *unconditionally* byte swapping the elements on the fly.
|
||||
@ -476,39 +776,23 @@ public final class Unsafe {
|
||||
* as discussed in {@link #getInt(Object,long)}. When the object reference is null,
|
||||
* the offset supplies an absolute base address.
|
||||
*
|
||||
* <em>Note:</em> It is the resposibility of the caller to make
|
||||
* sure arguments are checked before the methods are called. While
|
||||
* some rudimentary checks are performed on the input, the checks
|
||||
* are best effort and when performance is an overriding priority,
|
||||
* as when methods of this class are optimized by the runtime
|
||||
* compiler, some or all checks (if any) may be elided. Hence, the
|
||||
* caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @throws RuntimeException if any of the arguments is invalid
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public void copySwapMemory(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes, long elemSize) {
|
||||
if (bytes < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (elemSize != 2 && elemSize != 4 && elemSize != 8) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (bytes % elemSize != 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if ((srcBase == null && srcOffset == 0) ||
|
||||
(destBase == null && destOffset == 0)) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
// Must be off-heap, or primitive heap arrays
|
||||
if (srcBase != null && (srcOffset < 0 || !isPrimitiveArray(srcBase.getClass()))) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (destBase != null && (destOffset < 0 || !isPrimitiveArray(destBase.getClass()))) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// Sanity check size and offsets on 32-bit platforms. Most
|
||||
// significant 32 bits must be zero.
|
||||
if (ADDRESS_SIZE == 4 &&
|
||||
(bytes >>> 32 != 0 || srcOffset >>> 32 != 0 || destOffset >>> 32 != 0)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
copySwapMemoryChecks(srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
|
||||
|
||||
if (bytes == 0) {
|
||||
return;
|
||||
@ -517,6 +801,22 @@ public final class Unsafe {
|
||||
copySwapMemory0(srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
|
||||
}
|
||||
|
||||
private void copySwapMemoryChecks(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes, long elemSize) {
|
||||
checkSize(bytes);
|
||||
|
||||
if (elemSize != 2 && elemSize != 4 && elemSize != 8) {
|
||||
throw invalidInput();
|
||||
}
|
||||
if (bytes % elemSize != 0) {
|
||||
throw invalidInput();
|
||||
}
|
||||
|
||||
checkPrimitivePointer(srcBase, srcOffset);
|
||||
checkPrimitivePointer(destBase, destOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all elements from one block of memory to another block, byte swapping the
|
||||
* elements on the fly.
|
||||
@ -535,9 +835,40 @@ public final class Unsafe {
|
||||
* #allocateMemory} or {@link #reallocateMemory}. The address passed to
|
||||
* this method may be null, in which case no action is taken.
|
||||
*
|
||||
* <em>Note:</em> It is the resposibility of the caller to make
|
||||
* sure arguments are checked before the methods are called. While
|
||||
* some rudimentary checks are performed on the input, the checks
|
||||
* are best effort and when performance is an overriding priority,
|
||||
* as when methods of this class are optimized by the runtime
|
||||
* compiler, some or all checks (if any) may be elided. Hence, the
|
||||
* caller must not rely on the checks and corresponding
|
||||
* exceptions!
|
||||
*
|
||||
* @throws RuntimeException if any of the arguments is invalid
|
||||
*
|
||||
* @see #allocateMemory
|
||||
*/
|
||||
public native void freeMemory(long address);
|
||||
public void freeMemory(long address) {
|
||||
freeMemoryChecks(address);
|
||||
|
||||
if (address == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
freeMemory0(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the arguments to freeMemory
|
||||
*
|
||||
* @throws RuntimeException if the arguments are invalid
|
||||
* (<em>Note:</em> after optimization, invalid inputs may
|
||||
* go undetected, which will lead to unpredictable
|
||||
* behavior)
|
||||
*/
|
||||
private void freeMemoryChecks(long address) {
|
||||
checkPointer(null, address);
|
||||
}
|
||||
|
||||
/// random queries
|
||||
|
||||
@ -546,7 +877,7 @@ public final class Unsafe {
|
||||
* {@link #staticFieldOffset}, {@link #objectFieldOffset},
|
||||
* or {@link #arrayBaseOffset}.
|
||||
*/
|
||||
public static final int INVALID_FIELD_OFFSET = -1;
|
||||
public static final int INVALID_FIELD_OFFSET = -1;
|
||||
|
||||
/**
|
||||
* Reports the location of a given field in the storage allocation of its
|
||||
@ -566,7 +897,13 @@ public final class Unsafe {
|
||||
* must preserve all bits of static field offsets.
|
||||
* @see #getInt(Object, long)
|
||||
*/
|
||||
public native long objectFieldOffset(Field f);
|
||||
public long objectFieldOffset(Field f) {
|
||||
if (f == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return objectFieldOffset0(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the location of a given static field, in conjunction with {@link
|
||||
@ -585,7 +922,13 @@ public final class Unsafe {
|
||||
* this method reports its result as a long value.
|
||||
* @see #getInt(Object, long)
|
||||
*/
|
||||
public native long staticFieldOffset(Field f);
|
||||
public long staticFieldOffset(Field f) {
|
||||
if (f == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return staticFieldOffset0(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the location of a given static field, in conjunction with {@link
|
||||
@ -597,7 +940,13 @@ public final class Unsafe {
|
||||
* not be used in any way except as argument to the get and put routines in
|
||||
* this class.
|
||||
*/
|
||||
public native Object staticFieldBase(Field f);
|
||||
public Object staticFieldBase(Field f) {
|
||||
if (f == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return staticFieldBase0(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if the given class may need to be initialized. This is often
|
||||
@ -605,14 +954,26 @@ public final class Unsafe {
|
||||
* class.
|
||||
* @return false only if a call to {@code ensureClassInitialized} would have no effect
|
||||
*/
|
||||
public native boolean shouldBeInitialized(Class<?> c);
|
||||
public boolean shouldBeInitialized(Class<?> c) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return shouldBeInitialized0(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the given class has been initialized. This is often
|
||||
* needed in conjunction with obtaining the static field base of a
|
||||
* class.
|
||||
*/
|
||||
public native void ensureClassInitialized(Class<?> c);
|
||||
public void ensureClassInitialized(Class<?> c) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
ensureClassInitialized0(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the offset of the first element in the storage allocation of a
|
||||
@ -624,7 +985,14 @@ public final class Unsafe {
|
||||
* @see #getInt(Object, long)
|
||||
* @see #putInt(Object, long, int)
|
||||
*/
|
||||
public native int arrayBaseOffset(Class<?> arrayClass);
|
||||
public int arrayBaseOffset(Class<?> arrayClass) {
|
||||
if (arrayClass == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return arrayBaseOffset0(arrayClass);
|
||||
}
|
||||
|
||||
|
||||
/** The value of {@code arrayBaseOffset(boolean[].class)} */
|
||||
public static final int ARRAY_BOOLEAN_BASE_OFFSET
|
||||
@ -673,7 +1041,14 @@ public final class Unsafe {
|
||||
* @see #getInt(Object, long)
|
||||
* @see #putInt(Object, long, int)
|
||||
*/
|
||||
public native int arrayIndexScale(Class<?> arrayClass);
|
||||
public int arrayIndexScale(Class<?> arrayClass) {
|
||||
if (arrayClass == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return arrayIndexScale0(arrayClass);
|
||||
}
|
||||
|
||||
|
||||
/** The value of {@code arrayIndexScale(boolean[].class)} */
|
||||
public static final int ARRAY_BOOLEAN_INDEX_SCALE
|
||||
@ -717,10 +1092,12 @@ public final class Unsafe {
|
||||
* other primitive types (as stored in native memory blocks) is determined
|
||||
* fully by their information content.
|
||||
*/
|
||||
public native int addressSize();
|
||||
public int addressSize() {
|
||||
return ADDRESS_SIZE;
|
||||
}
|
||||
|
||||
/** The value of {@code addressSize()} */
|
||||
public static final int ADDRESS_SIZE = theUnsafe.addressSize();
|
||||
public static final int ADDRESS_SIZE = theUnsafe.addressSize0();
|
||||
|
||||
/**
|
||||
* Reports the size in bytes of a native memory page (whatever that is).
|
||||
@ -735,9 +1112,22 @@ public final class Unsafe {
|
||||
* Tells the VM to define a class, without security checks. By default, the
|
||||
* class loader and protection domain come from the caller's class.
|
||||
*/
|
||||
public native Class<?> defineClass(String name, byte[] b, int off, int len,
|
||||
ClassLoader loader,
|
||||
ProtectionDomain protectionDomain);
|
||||
public Class<?> defineClass(String name, byte[] b, int off, int len,
|
||||
ClassLoader loader,
|
||||
ProtectionDomain protectionDomain) {
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (len < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
return defineClass0(name, b, off, len, loader, protectionDomain);
|
||||
}
|
||||
|
||||
public native Class<?> defineClass0(String name, byte[] b, int off, int len,
|
||||
ClassLoader loader,
|
||||
ProtectionDomain protectionDomain);
|
||||
|
||||
/**
|
||||
* Defines a class but does not make it known to the class loader or system dictionary.
|
||||
@ -755,7 +1145,13 @@ public final class Unsafe {
|
||||
* @param data bytes of a class file
|
||||
* @param cpPatches where non-null entries exist, they replace corresponding CP entries in data
|
||||
*/
|
||||
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);
|
||||
public Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches) {
|
||||
if (hostClass == null || data == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return defineAnonymousClass0(hostClass, data, cpPatches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates an instance but does not run any constructor.
|
||||
@ -765,6 +1161,59 @@ public final class Unsafe {
|
||||
public native Object allocateInstance(Class<?> cls)
|
||||
throws InstantiationException;
|
||||
|
||||
/**
|
||||
* Allocates an array of a given type, but does not do zeroing.
|
||||
* <p>
|
||||
* This method should only be used in the very rare cases where a high-performance code
|
||||
* overwrites the destination array completely, and compilers cannot assist in zeroing elimination.
|
||||
* In an overwhelming majority of cases, a normal Java allocation should be used instead.
|
||||
* <p>
|
||||
* Users of this method are <b>required</b> to overwrite the initial (garbage) array contents
|
||||
* before allowing untrusted code, or code in other threads, to observe the reference
|
||||
* to the newly allocated array. In addition, the publication of the array reference must be
|
||||
* safe according to the Java Memory Model requirements.
|
||||
* <p>
|
||||
* The safest approach to deal with an uninitialized array is to keep the reference to it in local
|
||||
* variable at least until the initialization is complete, and then publish it <b>once</b>, either
|
||||
* by writing it to a <em>volatile</em> field, or storing it into a <em>final</em> field in constructor,
|
||||
* or issuing a {@link #storeFence} before publishing the reference.
|
||||
* <p>
|
||||
* @implnote This method can only allocate primitive arrays, to avoid garbage reference
|
||||
* elements that could break heap integrity.
|
||||
*
|
||||
* @param componentType array component type to allocate
|
||||
* @param length array size to allocate
|
||||
* @throws IllegalArgumentException if component type is null, or not a primitive class;
|
||||
* or the length is negative
|
||||
*/
|
||||
public Object allocateUninitializedArray(Class<?> componentType, int length) {
|
||||
if (componentType == null) {
|
||||
throw new IllegalArgumentException("Component type is null");
|
||||
}
|
||||
if (!componentType.isPrimitive()) {
|
||||
throw new IllegalArgumentException("Component type is not primitive");
|
||||
}
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Negative length");
|
||||
}
|
||||
return allocateUninitializedArray0(componentType, length);
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
private Object allocateUninitializedArray0(Class<?> componentType, int length) {
|
||||
// These fallbacks provide zeroed arrays, but intrinsic is not required to
|
||||
// return the zeroed arrays.
|
||||
if (componentType == byte.class) return new byte[length];
|
||||
if (componentType == boolean.class) return new boolean[length];
|
||||
if (componentType == short.class) return new short[length];
|
||||
if (componentType == char.class) return new char[length];
|
||||
if (componentType == int.class) return new int[length];
|
||||
if (componentType == float.class) return new float[length];
|
||||
if (componentType == long.class) return new long[length];
|
||||
if (componentType == double.class) return new double[length];
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Throws the exception without telling the verifier. */
|
||||
public native void throwException(Throwable ee);
|
||||
|
||||
@ -1290,7 +1739,13 @@ public final class Unsafe {
|
||||
* @return the number of samples actually retrieved; or -1
|
||||
* if the load average is unobtainable.
|
||||
*/
|
||||
public native int getLoadAverage(double[] loadavg, int nelems);
|
||||
public int getLoadAverage(double[] loadavg, int nelems) {
|
||||
if (nelems < 0 || nelems > 3 || nelems > loadavg.length) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
return getLoadAverage0(loadavg, nelems);
|
||||
}
|
||||
|
||||
// The following contain CAS-based Java implementations used on
|
||||
// platforms not supporting native instructions
|
||||
@ -1718,9 +2173,6 @@ public final class Unsafe {
|
||||
}
|
||||
|
||||
// JVM interface methods
|
||||
private native boolean unalignedAccess0();
|
||||
private native boolean isBigEndian0();
|
||||
|
||||
// BE is true iff the native endianness of this platform is big.
|
||||
private static final boolean BE = theUnsafe.isBigEndian0();
|
||||
|
||||
@ -1820,4 +2272,26 @@ public final class Unsafe {
|
||||
private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n) ; }
|
||||
private static int convEndian(boolean big, int n) { return big == BE ? n : Integer.reverseBytes(n) ; }
|
||||
private static long convEndian(boolean big, long n) { return big == BE ? n : Long.reverseBytes(n) ; }
|
||||
|
||||
|
||||
|
||||
private native long allocateMemory0(long bytes);
|
||||
private native long reallocateMemory0(long address, long bytes);
|
||||
private native void freeMemory0(long address);
|
||||
private native void setMemory0(Object o, long offset, long bytes, byte value);
|
||||
@HotSpotIntrinsicCandidate
|
||||
private native void copyMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
|
||||
private native void copySwapMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes, long elemSize);
|
||||
private native long objectFieldOffset0(Field f);
|
||||
private native long staticFieldOffset0(Field f);
|
||||
private native Object staticFieldBase0(Field f);
|
||||
private native boolean shouldBeInitialized0(Class<?> c);
|
||||
private native void ensureClassInitialized0(Class<?> c);
|
||||
private native int arrayBaseOffset0(Class<?> arrayClass);
|
||||
private native int arrayIndexScale0(Class<?> arrayClass);
|
||||
private native int addressSize0();
|
||||
private native Class<?> defineAnonymousClass0(Class<?> hostClass, byte[] data, Object[] cpPatches);
|
||||
private native int getLoadAverage0(double[] loadavg, int nelems);
|
||||
private native boolean unalignedAccess0();
|
||||
private native boolean isBigEndian0();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
667
jdk/test/jdk/internal/misc/Unsafe/CopyMemory.java
Normal file
667
jdk/test/jdk/internal/misc/Unsafe/CopyMemory.java
Normal file
@ -0,0 +1,667 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run negative tests, testing corner cases and the various exceptions
|
||||
*
|
||||
* @throws RuntimeException if an error is found
|
||||
*/
|
||||
private void testNegative() {
|
||||
long bufRaw = 0;
|
||||
|
||||
try {
|
||||
bufRaw = UNSAFE.allocateMemory(1024);
|
||||
long buf = alignUp(bufRaw, BASE_ALIGNMENT);
|
||||
short[] arr = new short[16];
|
||||
|
||||
// Check illegal sizes
|
||||
System.out.println("Testing negative size");
|
||||
try {
|
||||
UNSAFE.copyMemory(null, buf, null, buf, -1);
|
||||
throw new RuntimeException("copyMemory failed to throw IAE for size=-1");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
System.out.println("Testing negative srcOffset");
|
||||
try {
|
||||
// Check that negative srcOffset throws an IAE
|
||||
UNSAFE.copyMemory(arr, -1, arr, UNSAFE.arrayBaseOffset(arr.getClass()), 16);
|
||||
throw new RuntimeException("copyMemory failed to throw IAE for srcOffset=-1");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
System.out.println("Testing negative destOffset");
|
||||
try {
|
||||
// Check that negative dstOffset throws an IAE
|
||||
UNSAFE.copyMemory(arr, UNSAFE.arrayBaseOffset(arr.getClass()), arr, -1, 16);
|
||||
throw new RuntimeException("copyMemory failed to throw IAE for destOffset=-1");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
System.out.println("Testing reference array");
|
||||
try {
|
||||
// Check that a reference array destination throws IAE
|
||||
UNSAFE.copyMemory(null, buf, new Object[16], UNSAFE.arrayBaseOffset(Object[].class), 16);
|
||||
throw new RuntimeException("copyMemory failed to throw IAE");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
// Check that invalid source & dest pointers throw IAEs (only relevant on 32-bit platforms)
|
||||
if (UNSAFE.addressSize() == 4) {
|
||||
long invalidPtr = (long)1 << 35; // Pick a random bit in upper 32 bits
|
||||
|
||||
try {
|
||||
// Check that an invalid (not 32-bit clean) source pointer throws IAE
|
||||
UNSAFE.copyMemory(null, invalidPtr, null, buf, 16);
|
||||
throw new RuntimeException("copyMemory failed to throw IAE for srcOffset 0x" +
|
||||
Long.toHexString(invalidPtr));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
// Check that an invalid (not 32-bit clean) source pointer throws IAE
|
||||
UNSAFE.copyMemory(null, buf, null, invalidPtr, 16);
|
||||
throw new RuntimeException("copyMemory failed to throw IAE for destOffset 0x" +
|
||||
Long.toHexString(invalidPtr));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (bufRaw != 0) {
|
||||
UNSAFE.freeMemory(bufRaw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all tests
|
||||
*
|
||||
* @throws RuntimeException if an error is found
|
||||
*/
|
||||
private void test() {
|
||||
testPositive();
|
||||
testNegative();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -586,22 +586,6 @@ public class CopySwap {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Check that a NULL source throws NPE
|
||||
UNSAFE.copySwapMemory(null, 0, null, buf, 16, 2);
|
||||
throw new RuntimeException("copySwapMemory failed to throw NPE");
|
||||
} catch (NullPointerException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
// Check that a NULL destination throws NPE
|
||||
UNSAFE.copySwapMemory(null, buf, null, 0, 16, 2);
|
||||
throw new RuntimeException("copySwapMemory failed to throw NPE");
|
||||
} catch (NullPointerException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
// Check that a reference array destination throws IAE
|
||||
UNSAFE.copySwapMemory(null, buf, new Object[16], UNSAFE.arrayBaseOffset(Object[].class), 16, 8);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user