|
@@ -36,6 +36,7 @@ |
|
|
import java.lang.invoke.VarHandle; |
|
|
import java.nio.ByteOrder; |
|
|
import java.util.List; |
|
|
import java.util.Objects; |
|
|
|
|
|
/** |
|
|
* This class defines several factory methods for constructing and combining memory access var handles. |
|
@@ -132,6 +133,17 @@ private MemoryHandles() { |
|
|
private static final MethodHandle ADD_OFFSET; |
|
|
private static final MethodHandle ADD_STRIDE; |
|
|
|
|
|
private static final MethodHandle INT_TO_BYTE; |
|
|
private static final MethodHandle BYTE_TO_UNSIGNED_INT; |
|
|
private static final MethodHandle INT_TO_SHORT; |
|
|
private static final MethodHandle SHORT_TO_UNSIGNED_INT; |
|
|
private static final MethodHandle LONG_TO_BYTE; |
|
|
private static final MethodHandle BYTE_TO_UNSIGNED_LONG; |
|
|
private static final MethodHandle LONG_TO_SHORT; |
|
|
private static final MethodHandle SHORT_TO_UNSIGNED_LONG; |
|
|
private static final MethodHandle LONG_TO_INT; |
|
|
private static final MethodHandle INT_TO_UNSIGNED_LONG; |
|
|
|
|
|
static { |
|
|
try { |
|
|
LONG_TO_ADDRESS = MethodHandles.lookup().findStatic(MemoryHandles.class, "longToAddress", |
|
@@ -143,6 +155,27 @@ private MemoryHandles() { |
|
|
|
|
|
ADD_STRIDE = MethodHandles.lookup().findStatic(MemoryHandles.class, "addStride", |
|
|
MethodType.methodType(MemoryAddress.class, MemoryAddress.class, long.class, long.class)); |
|
|
|
|
|
INT_TO_BYTE = MethodHandles.explicitCastArguments(MethodHandles.identity(byte.class), |
|
|
MethodType.methodType(byte.class, int.class)); |
|
|
BYTE_TO_UNSIGNED_INT = MethodHandles.lookup().findStatic(Byte.class, "toUnsignedInt", |
|
|
MethodType.methodType(int.class, byte.class)); |
|
|
INT_TO_SHORT = MethodHandles.explicitCastArguments(MethodHandles.identity(short.class), |
|
|
MethodType.methodType(short.class, int.class)); |
|
|
SHORT_TO_UNSIGNED_INT = MethodHandles.lookup().findStatic(Short.class, "toUnsignedInt", |
|
|
MethodType.methodType(int.class, short.class)); |
|
|
LONG_TO_BYTE = MethodHandles.explicitCastArguments(MethodHandles.identity(byte.class), |
|
|
MethodType.methodType(byte.class, long.class)); |
|
|
BYTE_TO_UNSIGNED_LONG = MethodHandles.lookup().findStatic(Byte.class, "toUnsignedLong", |
|
|
MethodType.methodType(long.class, byte.class)); |
|
|
LONG_TO_SHORT = MethodHandles.explicitCastArguments(MethodHandles.identity(short.class), |
|
|
MethodType.methodType(short.class, long.class)); |
|
|
SHORT_TO_UNSIGNED_LONG = MethodHandles.lookup().findStatic(Short.class, "toUnsignedLong", |
|
|
MethodType.methodType(long.class, short.class)); |
|
|
LONG_TO_INT = MethodHandles.explicitCastArguments(MethodHandles.identity(int.class), |
|
|
MethodType.methodType(int.class, long.class)); |
|
|
INT_TO_UNSIGNED_LONG = MethodHandles.lookup().findStatic(Integer.class, "toUnsignedLong", |
|
|
MethodType.methodType(long.class, int.class)); |
|
|
} catch (Throwable ex) { |
|
|
throw new ExceptionInInitializerError(ex); |
|
|
} |
|
@@ -316,6 +349,72 @@ public static VarHandle asAddressVarHandle(VarHandle target) { |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Adapts a target var handle by narrowing incoming values and widening |
|
|
* outgoing values, to and from the given type, respectively. |
|
|
* <p> |
|
|
* The returned var handle can be used to conveniently treat unsigned |
|
|
* primitive data types as if they were a wider signed primitive type. For |
|
|
* example, it is often convenient to model an <i>unsigned short</i> as a |
|
|
* Java {@code int} to avoid dealing with negative values, which would be |
|
|
* the case if modeled as a Java {@code short}. The returned var handle |
|
|
* converts to and from wider primitive types, to a more narrow possibly |
|
|
* unsigned primitive type. |
|
|
* <p> |
|
|
* When calling e.g. {@link VarHandle#set(Object...)} on the resulting var |
|
|
* handle, the incoming value (of type {@code adaptedType}) is converted by a |
|
|
* <i>narrowing primitive conversion</i> and then passed to the {@code |
|
|
* target} var handle. A narrowing primitive conversion may lose information |
|
|
* about the overall magnitude of a numeric value. Conversely, when calling |
|
|
* e.g. {@link VarHandle#get(Object...)} on the resulting var handle, the |
|
|
* returned value obtained from the {@code target} var handle is converted |
|
|
* by a <i>unsigned widening conversion</i> before being returned to the |
|
|
* caller. In an unsigned widening conversion the high-order bits greater |
|
|
* than that of the {@code target} carrier type are zero, and the low-order |
|
|
* bits (equal to the width of the {@code target} carrier type) are equal to |
|
|
* the bits of the value obtained from the {@code target} var handle. |
|
|
* <p> |
|
|
* The returned var handle will feature the variable type {@code adaptedType}, |
|
|
* and the same access coordinates, the same access modes (see {@link |
|
|
* java.lang.invoke.VarHandle.AccessMode}, and the same atomic access |
|
|
* guarantees, as those featured by the {@code target} var handle. |
|
|
* |
|
|
* @param target the memory access var handle to be adapted |
|
|
* @param adaptedType the adapted type |
|
|
* @returns the adapted var handle. |
|
|
* @throws IllegalArgumentException if the carrier type of {@code target} |
|
|
* is not one of {@code byte}, {@code short}, or {@code int}; if {@code |
|
|
* adaptedType} is not one of {@code int}, or {@code long}; if the bitwidth |
|
|
* of the {@code adaptedType} is not greater than that of the {@code target} |
|
|
* carrier type |
|
|
* @throws NullPointerException if either of {@code target} or {@code |
|
|
* adaptedType} is null |
|
|
* |
|
|
* @jls 5.1.3 Narrowing Primitive Conversion |
|
|
*/ |
|
|
public static VarHandle asUnsigned(VarHandle target, final Class<?> adaptedType) { |
|
|
Objects.requireNonNull(target); |
|
|
Objects.requireNonNull(adaptedType); |
|
|
final Class<?> carrier = target.varType(); |
|
|
checkWidenable(carrier); |
|
|
checkNarrowable(adaptedType); |
|
|
checkTargetWiderThanCarrier(carrier, adaptedType); |
|
|
|
|
|
if (adaptedType == int.class && carrier == byte.class) { |
|
|
return filterValue(target, INT_TO_BYTE, BYTE_TO_UNSIGNED_INT); |
|
|
} else if (adaptedType == int.class && carrier == short.class) { |
|
|
return filterValue(target, INT_TO_SHORT, SHORT_TO_UNSIGNED_INT); |
|
|
} else if (adaptedType == long.class && carrier == byte.class) { |
|
|
return filterValue(target, LONG_TO_BYTE, BYTE_TO_UNSIGNED_LONG); |
|
|
} else if (adaptedType == long.class && carrier == short.class) { |
|
|
return filterValue(target, LONG_TO_SHORT, SHORT_TO_UNSIGNED_LONG); |
|
|
} else if (adaptedType == long.class && carrier == int.class) { |
|
|
return filterValue(target, LONG_TO_INT, INT_TO_UNSIGNED_LONG); |
|
|
} else { |
|
|
throw new InternalError("should not reach here"); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Adapts a target var handle by pre-processing incoming and outgoing values using a pair of unary filter functions. |
|
|
* <p> |
|
@@ -532,6 +631,25 @@ private static long carrierSize(Class<?> carrier) { |
|
|
return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new); |
|
|
} |
|
|
|
|
|
private static void checkWidenable(Class<?> carrier) { |
|
|
if (!(carrier == byte.class || carrier == short.class || carrier == int.class)) { |
|
|
throw new IllegalArgumentException("illegal carrier:" + carrier.getSimpleName()); |
|
|
} |
|
|
} |
|
|
|
|
|
private static void checkNarrowable(Class<?> type) { |
|
|
if (!(type == int.class || type == long.class)) { |
|
|
throw new IllegalArgumentException("illegal adapter type: " + type.getSimpleName()); |
|
|
} |
|
|
} |
|
|
|
|
|
private static void checkTargetWiderThanCarrier(Class<?> carrier, Class<?> target) { |
|
|
if (Wrapper.forPrimitiveType(target).bitWidth() <= Wrapper.forPrimitiveType(carrier).bitWidth()) { |
|
|
throw new IllegalArgumentException( |
|
|
target.getSimpleName() + " is not wider than: " + carrier.getSimpleName()); |
|
|
} |
|
|
} |
|
|
|
|
|
private static MemoryAddress longToAddress(long value) { |
|
|
return MemoryAddress.ofLong(value); |
|
|
} |
|
|