8246095: Tweaks to memory access API
Add more user friendly API points to the foreign memory acesss API Reviewed-by: chegar, psandoz
This commit is contained in:
parent
eec7750e55
commit
f1e1cb7055
src
java.base/share/classes
jdk.incubator.foreign/share/classes/jdk
test
jdk/java
foreign
TestAdaptVarHandles.javaTestByteBuffer.javaTestLayoutPaths.javaTestMemoryCopy.javaTestMemoryHandleAsUnsigned.javaTestMismatch.javaTestNative.javaTestSegments.javaTestSpliterator.java
util/stream/test/org/openjdk/tests/java/util/stream
micro/org/openjdk/bench/jdk/incubator/foreign
@ -989,6 +989,57 @@ class LambdaFormEditor {
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm collectReturnValueForm(MethodType combinerType) {
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
int argPos = lambdaForm.arity();
|
||||
int exprPos = lambdaForm.names.length;
|
||||
|
||||
BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
|
||||
BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE);
|
||||
|
||||
// The newly created LF will run with a different BMH.
|
||||
// Switch over any pre-existing BMH field references to the new BMH class.
|
||||
Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values
|
||||
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
|
||||
Name newBaseAddress = oldBaseAddress.withConstraint(newData);
|
||||
buf.renameParameter(0, newBaseAddress);
|
||||
|
||||
// Now we set up the call to the filter
|
||||
Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
|
||||
|
||||
Object[] combinerArgs = new Object[combinerArity + 1];
|
||||
combinerArgs[0] = getCombiner; // first (synthetic) argument should be the MH that acts as a target of the invoke
|
||||
|
||||
// set up additional adapter parameters (in case the combiner is not a unary function)
|
||||
Name[] newParams = new Name[combinerArity - 1]; // last combiner parameter is the return adapter
|
||||
for (int i = 0; i < newParams.length; i++) {
|
||||
newParams[i] = new Name(argPos + i, basicType(combinerType.parameterType(i)));
|
||||
}
|
||||
|
||||
// set up remaining filter parameters to point to the corresponding adapter parameters (see above)
|
||||
System.arraycopy(newParams, 0,
|
||||
combinerArgs, 1, combinerArity - 1);
|
||||
|
||||
// the last filter argument is set to point at the result of the target method handle
|
||||
combinerArgs[combinerArity] = buf.name(lambdaForm.names.length - 1);
|
||||
Name callCombiner = new Name(combinerType, combinerArgs);
|
||||
|
||||
// insert the two new expressions
|
||||
buf.insertExpression(exprPos, getCombiner);
|
||||
buf.insertExpression(exprPos + 1, callCombiner);
|
||||
|
||||
// insert additional arguments
|
||||
int insPos = argPos;
|
||||
for (Name newParam : newParams) {
|
||||
buf.insertParameter(insPos++, newParam);
|
||||
}
|
||||
|
||||
buf.setResult(callCombiner);
|
||||
return buf.endEdit();
|
||||
}
|
||||
|
||||
LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
byte kind = (dropResult ? FOLD_ARGS_TO_VOID : FOLD_ARGS);
|
||||
|
@ -5539,6 +5539,40 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the return value of a target method handle with a filter function. The filter function is
|
||||
* applied to the return value of the original handle; if the filter specifies more than one parameters,
|
||||
* then any remaining parameter is appended to the adapter handle. In other words, the adaptation works
|
||||
* as follows:
|
||||
* <blockquote><pre>{@code
|
||||
* T target(A...)
|
||||
* V filter(B... , T)
|
||||
* V adapter(A... a, B... b) {
|
||||
* T t = target(a...);
|
||||
* return filter(b..., t);
|
||||
* }</pre></blockquote>
|
||||
* <p>
|
||||
* If the filter handle is a unary function, then this method behaves like {@link #filterReturnValue(MethodHandle, MethodHandle)}.
|
||||
*
|
||||
* @param target the target method handle
|
||||
* @param filter the filter method handle
|
||||
* @return the adapter method handle
|
||||
*/
|
||||
/* package */ static MethodHandle collectReturnValue(MethodHandle target, MethodHandle filter) {
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm lform = result.editor().collectReturnValueForm(filterType.basicType());
|
||||
MethodType newType = targetType.changeReturnType(filterType.returnType());
|
||||
if (filterType.parameterList().size() > 1) {
|
||||
for (int i = 0 ; i < filterType.parameterList().size() - 1 ; i++) {
|
||||
newType = newType.appendParameterTypes(filterType.parameterType(i));
|
||||
}
|
||||
}
|
||||
result = result.copyWithExtendL(newType, lform, filter);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a target method handle by pre-processing
|
||||
* some of its arguments, and then calling the target with
|
||||
|
@ -356,43 +356,97 @@ final class VarHandles {
|
||||
noCheckedExceptions(filterToTarget);
|
||||
noCheckedExceptions(filterFromTarget);
|
||||
|
||||
List<Class<?>> newCoordinates = new ArrayList<>();
|
||||
List<Class<?>> additionalCoordinates = new ArrayList<>();
|
||||
newCoordinates.addAll(target.coordinateTypes());
|
||||
|
||||
//check that from/to filters have right signatures
|
||||
if (filterFromTarget.type().parameterCount() != 1) {
|
||||
if (filterFromTarget.type().parameterCount() != filterToTarget.type().parameterCount()) {
|
||||
throw newIllegalArgumentException("filterFromTarget and filterToTarget have different arity", filterFromTarget.type(), filterToTarget.type());
|
||||
} else if (filterFromTarget.type().parameterCount() < 1) {
|
||||
throw newIllegalArgumentException("filterFromTarget filter type has wrong arity", filterFromTarget.type());
|
||||
} else if (filterToTarget.type().parameterCount() != 1) {
|
||||
} else if (filterToTarget.type().parameterCount() < 1) {
|
||||
throw newIllegalArgumentException("filterToTarget filter type has wrong arity", filterFromTarget.type());
|
||||
} else if (filterFromTarget.type().parameterType(0) != filterToTarget.type().returnType() ||
|
||||
filterToTarget.type().parameterType(0) != filterFromTarget.type().returnType()) {
|
||||
} else if (filterFromTarget.type().lastParameterType() != filterToTarget.type().returnType() ||
|
||||
filterToTarget.type().lastParameterType() != filterFromTarget.type().returnType()) {
|
||||
throw newIllegalArgumentException("filterFromTarget and filterToTarget filter types do not match", filterFromTarget.type(), filterToTarget.type());
|
||||
} else if (target.varType() != filterFromTarget.type().parameterType(0)) {
|
||||
} else if (target.varType() != filterFromTarget.type().lastParameterType()) {
|
||||
throw newIllegalArgumentException("filterFromTarget filter type does not match target var handle type", filterFromTarget.type(), target.varType());
|
||||
} else if (target.varType() != filterToTarget.type().returnType()) {
|
||||
throw newIllegalArgumentException("filterFromTarget filter type does not match target var handle type", filterToTarget.type(), target.varType());
|
||||
} else if (filterFromTarget.type().parameterCount() > 1) {
|
||||
for (int i = 0 ; i < filterFromTarget.type().parameterCount() - 1 ; i++) {
|
||||
if (filterFromTarget.type().parameterType(i) != filterToTarget.type().parameterType(i)) {
|
||||
throw newIllegalArgumentException("filterFromTarget and filterToTarget filter types do not match", filterFromTarget.type(), filterToTarget.type());
|
||||
} else {
|
||||
newCoordinates.add(filterFromTarget.type().parameterType(i));
|
||||
additionalCoordinates.add((filterFromTarget.type().parameterType(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new IndirectVarHandle(target, filterFromTarget.type().returnType(), target.coordinateTypes().toArray(new Class<?>[0]),
|
||||
return new IndirectVarHandle(target, filterFromTarget.type().returnType(), newCoordinates.toArray(new Class<?>[0]),
|
||||
(mode, modeHandle) -> {
|
||||
int lastParameterPos = modeHandle.type().parameterCount() - 1;
|
||||
return switch (mode.at) {
|
||||
case GET -> MethodHandles.filterReturnValue(modeHandle, filterFromTarget);
|
||||
case SET -> MethodHandles.filterArgument(modeHandle, lastParameterPos, filterToTarget);
|
||||
case GET -> MethodHandles.collectReturnValue(modeHandle, filterFromTarget);
|
||||
case SET -> MethodHandles.collectArguments(modeHandle, lastParameterPos, filterToTarget);
|
||||
case GET_AND_UPDATE -> {
|
||||
MethodHandle adapter = MethodHandles.filterReturnValue(modeHandle, filterFromTarget);
|
||||
yield MethodHandles.filterArgument(adapter, lastParameterPos, filterToTarget);
|
||||
MethodHandle adapter = MethodHandles.collectReturnValue(modeHandle, filterFromTarget);
|
||||
MethodHandle res = MethodHandles.collectArguments(adapter, lastParameterPos, filterToTarget);
|
||||
if (additionalCoordinates.size() > 0) {
|
||||
res = joinDuplicateArgs(res, lastParameterPos,
|
||||
lastParameterPos + additionalCoordinates.size() + 1,
|
||||
additionalCoordinates.size());
|
||||
}
|
||||
yield res;
|
||||
}
|
||||
case COMPARE_AND_EXCHANGE -> {
|
||||
MethodHandle adapter = MethodHandles.filterReturnValue(modeHandle, filterFromTarget);
|
||||
adapter = MethodHandles.filterArgument(adapter, lastParameterPos, filterToTarget);
|
||||
yield MethodHandles.filterArgument(adapter, lastParameterPos - 1, filterToTarget);
|
||||
MethodHandle adapter = MethodHandles.collectReturnValue(modeHandle, filterFromTarget);
|
||||
adapter = MethodHandles.collectArguments(adapter, lastParameterPos, filterToTarget);
|
||||
if (additionalCoordinates.size() > 0) {
|
||||
adapter = joinDuplicateArgs(adapter, lastParameterPos,
|
||||
lastParameterPos + additionalCoordinates.size() + 1,
|
||||
additionalCoordinates.size());
|
||||
}
|
||||
MethodHandle res = MethodHandles.collectArguments(adapter, lastParameterPos - 1, filterToTarget);
|
||||
if (additionalCoordinates.size() > 0) {
|
||||
res = joinDuplicateArgs(res, lastParameterPos - 1,
|
||||
lastParameterPos + additionalCoordinates.size(),
|
||||
additionalCoordinates.size());
|
||||
}
|
||||
yield res;
|
||||
}
|
||||
case COMPARE_AND_SET -> {
|
||||
MethodHandle adapter = MethodHandles.filterArgument(modeHandle, lastParameterPos, filterToTarget);
|
||||
yield MethodHandles.filterArgument(adapter, lastParameterPos - 1, filterToTarget);
|
||||
MethodHandle adapter = MethodHandles.collectArguments(modeHandle, lastParameterPos, filterToTarget);
|
||||
MethodHandle res = MethodHandles.collectArguments(adapter, lastParameterPos - 1, filterToTarget);
|
||||
if (additionalCoordinates.size() > 0) {
|
||||
res = joinDuplicateArgs(res, lastParameterPos - 1,
|
||||
lastParameterPos + additionalCoordinates.size(),
|
||||
additionalCoordinates.size());
|
||||
}
|
||||
yield res;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private static MethodHandle joinDuplicateArgs(MethodHandle handle, int originalStart, int dropStart, int length) {
|
||||
int[] perms = new int[handle.type().parameterCount()];
|
||||
for (int i = 0 ; i < dropStart; i++) {
|
||||
perms[i] = i;
|
||||
}
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
perms[dropStart + i] = originalStart + i;
|
||||
}
|
||||
for (int i = dropStart + length ; i < perms.length ; i++) {
|
||||
perms[i] = i - length;
|
||||
}
|
||||
return MethodHandles.permuteArguments(handle,
|
||||
handle.type().dropParameterTypes(dropStart, dropStart + length),
|
||||
perms);
|
||||
}
|
||||
|
||||
public static VarHandle filterCoordinates(VarHandle target, int pos, MethodHandle... filters) {
|
||||
Objects.nonNull(target);
|
||||
Objects.nonNull(filters);
|
||||
@ -542,17 +596,23 @@ final class VarHandles {
|
||||
private static void noCheckedExceptions(MethodHandle handle) {
|
||||
if (handle instanceof DirectMethodHandle) {
|
||||
DirectMethodHandle directHandle = (DirectMethodHandle)handle;
|
||||
MethodHandleInfo info = MethodHandles.Lookup.IMPL_LOOKUP.revealDirect(directHandle);
|
||||
Class<?>[] exceptionTypes = switch (info.getReferenceKind()) {
|
||||
case MethodHandleInfo.REF_invokeInterface, MethodHandleInfo.REF_invokeSpecial,
|
||||
MethodHandleInfo.REF_invokeStatic, MethodHandleInfo.REF_invokeVirtual ->
|
||||
info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP).getExceptionTypes();
|
||||
case MethodHandleInfo.REF_newInvokeSpecial ->
|
||||
info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP).getExceptionTypes();
|
||||
case MethodHandleInfo.REF_getField, MethodHandleInfo.REF_getStatic,
|
||||
MethodHandleInfo.REF_putField, MethodHandleInfo.REF_putStatic -> null;
|
||||
default -> throw new AssertionError("Cannot get here");
|
||||
};
|
||||
byte refKind = directHandle.member.getReferenceKind();
|
||||
MethodHandleInfo info = new InfoFromMemberName(
|
||||
MethodHandles.Lookup.IMPL_LOOKUP,
|
||||
directHandle.member,
|
||||
refKind);
|
||||
final Class<?>[] exceptionTypes;
|
||||
if (MethodHandleNatives.refKindIsMethod(refKind)) {
|
||||
exceptionTypes = info.reflectAs(Method.class, MethodHandles.Lookup.IMPL_LOOKUP)
|
||||
.getExceptionTypes();
|
||||
} else if (MethodHandleNatives.refKindIsField(refKind)) {
|
||||
exceptionTypes = null;
|
||||
} else if (MethodHandleNatives.refKindIsConstructor(refKind)) {
|
||||
exceptionTypes = info.reflectAs(Constructor.class, MethodHandles.Lookup.IMPL_LOOKUP)
|
||||
.getExceptionTypes();
|
||||
} else {
|
||||
throw new AssertionError("Cannot get here");
|
||||
}
|
||||
if (exceptionTypes != null) {
|
||||
if (Stream.of(exceptionTypes).anyMatch(VarHandles::isCheckedException)) {
|
||||
throw newIllegalArgumentException("Cannot adapt a var handle with a method handle which throws checked exceptions");
|
||||
|
@ -160,6 +160,32 @@ public class ArraysSupport {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mismatch over long lengths.
|
||||
*/
|
||||
public static long vectorizedMismatchLarge(Object a, long aOffset,
|
||||
Object b, long bOffset,
|
||||
long length,
|
||||
int log2ArrayIndexScale) {
|
||||
long off = 0;
|
||||
long remaining = length;
|
||||
int i ;
|
||||
while (remaining > 7) {
|
||||
int size = (int) Math.min(Integer.MAX_VALUE, remaining);
|
||||
i = vectorizedMismatch(
|
||||
a, aOffset + off,
|
||||
b, bOffset + off,
|
||||
size, log2ArrayIndexScale);
|
||||
if (i >= 0)
|
||||
return off + i;
|
||||
|
||||
i = size - ~i;
|
||||
off += i;
|
||||
remaining -= i;
|
||||
}
|
||||
return ~off;
|
||||
}
|
||||
|
||||
// Booleans
|
||||
// Each boolean element takes up one byte
|
||||
|
||||
|
@ -231,6 +231,8 @@ module java.base {
|
||||
jdk.internal.vm.ci,
|
||||
jdk.incubator.foreign,
|
||||
jdk.unsupported;
|
||||
exports jdk.internal.util to
|
||||
jdk.incubator.foreign;
|
||||
exports jdk.internal.util.jar to
|
||||
jdk.jartool;
|
||||
exports jdk.internal.util.xml to
|
||||
|
@ -31,7 +31,7 @@ import java.nio.file.Path;
|
||||
/**
|
||||
* A mapped memory segment, that is, a memory segment backed by memory-mapped file.
|
||||
*
|
||||
* <p> Mapped memory segments are created via the {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}.
|
||||
* <p> Mapped memory segments are created via the {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}.
|
||||
* Mapped memory segments behave like ordinary segments, but provide additional capabilities to manipulate memory-mapped
|
||||
* memory regions, such as {@link #force()} and {@link #load()}.
|
||||
* <p>
|
||||
|
@ -115,33 +115,6 @@ public interface MemoryAddress {
|
||||
@Override
|
||||
int hashCode();
|
||||
|
||||
/**
|
||||
* Perform bulk copy from source address to target address. More specifically, the bytes at addresses {@code src}
|
||||
* through {@code src.addOffset(bytes - 1)} are copied into addresses {@code dst} through {@code dst.addOffset(bytes - 1)}.
|
||||
* If the source and address ranges overlap, then the copying is performed as if the bytes at addresses {@code src}
|
||||
* through {@code src.addOffset(bytes - 1)} were first copied into a temporary segment with size {@code bytes},
|
||||
* and then the contents of the temporary segment were copied into the bytes at addresses {@code dst} through
|
||||
* {@code dst.addOffset(bytes - 1)}.
|
||||
* <p>
|
||||
* The result of a bulk copy is unspecified if, in the uncommon case, the source and target address ranges do not
|
||||
* overlap, but refer to overlapping regions of the same backing storage using different addresses. For example,
|
||||
* this may occur if the same file is {@link MemorySegment#mapFromPath mapped} to two segments.
|
||||
*
|
||||
* @param src the source address.
|
||||
* @param dst the target address.
|
||||
* @param bytes the number of bytes to be copied.
|
||||
* @throws IndexOutOfBoundsException if {@code bytes < 0}, or if it is greater than the size of the segments
|
||||
* associated with either {@code src} or {@code dst}.
|
||||
* @throws IllegalStateException if either the source address or the target address belong to memory segments
|
||||
* which have been already closed, or if access occurs from a thread other than the thread owning either segment.
|
||||
* @throws UnsupportedOperationException if either {@code src} or {@code dst} do not feature required access modes;
|
||||
* more specifically, {@code src} should be associated with a segment with {@link MemorySegment#READ} access mode,
|
||||
* while {@code dst} should be associated with a segment with {@link MemorySegment#WRITE} access mode.
|
||||
*/
|
||||
static void copy(MemoryAddress src, MemoryAddress dst, long bytes) {
|
||||
MemoryAddressImpl.copy((MemoryAddressImpl)src, (MemoryAddressImpl)dst, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* The <em>unchecked</em> memory address instance modelling the {@code NULL} address. This address is <em>not</em> backed by
|
||||
* a memory segment and hence it cannot be dereferenced.
|
||||
|
@ -36,6 +36,7 @@ import java.lang.invoke.MethodType;
|
||||
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 @@ public final class 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 @@ public final class 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);
|
||||
}
|
||||
@ -317,21 +350,93 @@ public final class MemoryHandles {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a target var handle by pre-processing incoming and outgoing values using a pair of unary filter functions.
|
||||
* 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}. This is illustrated in the following example:
|
||||
* <blockquote><pre>{@code
|
||||
MemorySegment segment = MemorySegment.allocateNative(2);
|
||||
VarHandle SHORT_VH = MemoryLayouts.JAVA_SHORT.varHandle(short.class);
|
||||
VarHandle INT_VH = MemoryHandles.asUnsigned(SHORT_VH, int.class);
|
||||
SHORT_VH.set(segment.baseAddress(), (short)-1);
|
||||
INT_VH.get(segment.baseAddress()); // returns 65535
|
||||
* }</pre></blockquote>
|
||||
* <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
|
||||
* @return 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 filter functions.
|
||||
* <p>
|
||||
* When calling e.g. {@link VarHandle#set(Object...)} on the resulting var handle, the incoming value (of type {@code T}, where
|
||||
* {@code T} is the parameter type of the first filter function) is processed using the first filter and then passed
|
||||
* {@code T} is the <em>last</em> parameter type of the first filter function) is processed using the first filter and then passed
|
||||
* to the target var handle.
|
||||
* Conversely, when calling e.g. {@link VarHandle#get(Object...)} on the resulting var handle, the return value obtained from
|
||||
* the target var handle (of type {@code T}, where {@code T} is the parameter type of the second filter function)
|
||||
* the target var handle (of type {@code T}, where {@code T} is the <em>last</em> parameter type of the second filter function)
|
||||
* is processed using the second filter and returned to the caller. More advanced access mode types, such as
|
||||
* {@link java.lang.invoke.VarHandle.AccessMode#COMPARE_AND_EXCHANGE} might apply both filters at the same time.
|
||||
* <p>
|
||||
* For the filters to be well formed, their types must be of the form {@code S -> T} and {@code T -> S},
|
||||
* respectively, where {@code T} is the type of the target var handle. If this is the case, the resulting var handle will
|
||||
* have type {@code S}.
|
||||
* For the boxing and unboxing filters to be well formed, their types must be of the form {@code (A... , S) -> T} and
|
||||
* {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle. If this is the case,
|
||||
* the resulting var handle will have type {@code S} and will feature the additional coordinates {@code A...} (which
|
||||
* will be appended to the coordinates of the target var handle).
|
||||
* <p>
|
||||
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode}) and
|
||||
* The resulting var handle will feature the same access modes (see {@link java.lang.invoke.VarHandle.AccessMode} and
|
||||
* atomic access guarantees as those featured by the target var handle.
|
||||
*
|
||||
* @param target the target var handle
|
||||
@ -340,7 +445,7 @@ public final class MemoryHandles {
|
||||
* @return an adapter var handle which accepts a new type, performing the provided boxing/unboxing conversions.
|
||||
* @throws NullPointerException if either {@code target}, {@code filterToTarget} or {@code filterFromTarget} are {@code == null}.
|
||||
* @throws IllegalArgumentException if {@code filterFromTarget} and {@code filterToTarget} are not well-formed, that is, they have types
|
||||
* other than {@code S -> T} and {@code T -> S}, respectively, where {@code T} is the type of the target var handle,
|
||||
* other than {@code (A... , S) -> T} and {@code (A... , T) -> S}, respectively, where {@code T} is the type of the target var handle,
|
||||
* or if either {@code filterFromTarget} or {@code filterToTarget} throws any checked exceptions.
|
||||
*/
|
||||
public static VarHandle filterValue(VarHandle target, MethodHandle filterToTarget, MethodHandle filterFromTarget) {
|
||||
@ -531,6 +636,25 @@ public final class MemoryHandles {
|
||||
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);
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ import java.util.stream.Stream;
|
||||
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
|
||||
* <p>
|
||||
* Layout paths are for example useful in order to obtain offsets of arbitrarily nested layouts inside another layout
|
||||
* (see {@link MemoryLayout#offset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected
|
||||
* (see {@link MemoryLayout#bitOffset(PathElement...)}), to quickly obtain a memory access handle corresponding to the selected
|
||||
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}), to select an arbitrarily nested layout inside
|
||||
* another layout (see {@link MemoryLayout#select(PathElement...)}, or to transform a nested layout element inside
|
||||
* another layout (see {@link MemoryLayout#map(UnaryOperator, PathElement...)}).
|
||||
@ -112,9 +112,9 @@ SequenceLayout seq = MemoryLayout.ofSequence(5,
|
||||
));
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* We can obtain the offset of the member layout named <code>value</code> from <code>seq</code>, as follows:
|
||||
* We can obtain the offset, in bits, of the member layout named <code>value</code> from <code>seq</code>, as follows:
|
||||
* <blockquote><pre>{@code
|
||||
long valueOffset = seq.addOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
|
||||
long valueOffset = seq.bitOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* Similarly, we can select the member layout named {@code value}, as follows:
|
||||
@ -342,10 +342,30 @@ public interface MemoryLayout extends Constable {
|
||||
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
|
||||
*/
|
||||
default long offset(PathElement... elements) {
|
||||
default long bitOffset(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offset, EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the offset, in bytes, of the layout selected by a given layout path, where the path is considered rooted in this
|
||||
* layout.
|
||||
*
|
||||
* @apiNote if the layout path has one (or more) free dimensions,
|
||||
* the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
|
||||
*
|
||||
* @param elements the layout path elements.
|
||||
* @return The offset, in bytes, of the layout selected by the layout path in {@code elements}.
|
||||
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
|
||||
* layout path contains one or more path elements that select multiple sequence element indices
|
||||
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size,
|
||||
* or if {@code bitOffset(elements)} is not a multiple of 8.
|
||||
*/
|
||||
default long byteOffset(PathElement... elements) {
|
||||
return Utils.bitsToBytesOrThrow(bitOffset(elements),
|
||||
() -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
|
||||
* where the path is considered rooted in this layout.
|
||||
|
@ -74,7 +74,7 @@ import java.util.function.Consumer;
|
||||
* by native memory.
|
||||
* <p>
|
||||
* Finally, it is also possible to obtain a memory segment backed by a memory-mapped file using the factory method
|
||||
* {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>
|
||||
* {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>
|
||||
* (see {@link MappedMemorySegment}).
|
||||
*
|
||||
* <h2>Closing a memory segment</h2>
|
||||
@ -286,6 +286,82 @@ public interface MemorySegment extends AutoCloseable {
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Fills a value into this memory segment.
|
||||
* <p>
|
||||
* More specifically, the given value is filled into each address of this
|
||||
* segment. Equivalent to (but likely more efficient than) the following code:
|
||||
*
|
||||
* <pre>{@code
|
||||
byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
|
||||
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
|
||||
for (long l = 0; l < segment.byteSize(); l++) {
|
||||
byteHandle.set(segment.baseAddress(), l, value);
|
||||
}
|
||||
* }</pre>
|
||||
*
|
||||
* without any regard or guarantees on the ordering of particular memory
|
||||
* elements being set.
|
||||
* <p>
|
||||
* Fill can be useful to initialize or reset the memory of a segment.
|
||||
*
|
||||
* @param value the value to fill into this segment
|
||||
* @return this memory segment
|
||||
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
|
||||
* thread owning this segment
|
||||
* @throws UnsupportedOperationException if this segment does not support the {@link #WRITE} access mode
|
||||
*/
|
||||
MemorySegment fill(byte value);
|
||||
|
||||
/**
|
||||
* Performs a bulk copy from given source segment to this segment. More specifically, the bytes at
|
||||
* offset {@code 0} through {@code src.byteSize() - 1} in the source segment are copied into this segment
|
||||
* at offset {@code 0} through {@code src.byteSize() - 1}.
|
||||
* If the source segment overlaps with this segment, then the copying is performed as if the bytes at
|
||||
* offset {@code 0} through {@code src.byteSize() - 1} in the source segment were first copied into a
|
||||
* temporary segment with size {@code bytes}, and then the contents of the temporary segment were copied into
|
||||
* this segment at offset {@code 0} through {@code src.byteSize() - 1}.
|
||||
* <p>
|
||||
* The result of a bulk copy is unspecified if, in the uncommon case, the source segment and this segment
|
||||
* do not overlap, but refer to overlapping regions of the same backing storage using different addresses.
|
||||
* For example, this may occur if the same file is {@link MemorySegment#mapFromPath mapped} to two segments.
|
||||
*
|
||||
* @param src the source segment.
|
||||
* @throws IndexOutOfBoundsException if {@code src.byteSize() > this.byteSize()}.
|
||||
* @throws IllegalStateException if either the source segment or this segment have been already closed,
|
||||
* or if access occurs from a thread other than the thread owning either segment.
|
||||
* @throws UnsupportedOperationException if either the source segment or this segment do not feature required access modes;
|
||||
* more specifically, {@code src} should feature at least the {@link MemorySegment#READ} access mode,
|
||||
* while this segment should feature at least the {@link MemorySegment#WRITE} access mode.
|
||||
*/
|
||||
void copyFrom(MemorySegment src);
|
||||
|
||||
/**
|
||||
* Finds and returns the offset, in bytes, of the first mismatch between
|
||||
* this segment and a given other segment. The offset is relative to the
|
||||
* {@link #baseAddress() base address} of each segment and will be in the
|
||||
* range of 0 (inclusive) up to the {@link #byteSize() size} (in bytes) of
|
||||
* the smaller memory segment (exclusive).
|
||||
* <p>
|
||||
* If the two segments share a common prefix then the returned offset is
|
||||
* the length of the common prefix and it follows that there is a mismatch
|
||||
* between the two segments at that offset within the respective segments.
|
||||
* If one segment is a proper prefix of the other then the returned offset is
|
||||
* the smaller of the segment sizes, and it follows that the offset is only
|
||||
* valid for the larger segment. Otherwise, there is no mismatch and {@code
|
||||
* -1} is returned.
|
||||
*
|
||||
* @param other the segment to be tested for a mismatch with this segment
|
||||
* @return the relative offset, in bytes, of the first mismatch between this
|
||||
* and the given other segment, otherwise -1 if no mismatch
|
||||
* @throws IllegalStateException if either this segment of the other segment
|
||||
* have been already closed, or if access occurs from a thread other than the
|
||||
* thread owning either segment
|
||||
* @throws UnsupportedOperationException if either this segment or the other
|
||||
* segment does not feature at least the {@link MemorySegment#READ} access mode
|
||||
*/
|
||||
long mismatch(MemorySegment other);
|
||||
|
||||
/**
|
||||
* Wraps this segment in a {@link ByteBuffer}. Some of the properties of the returned buffer are linked to
|
||||
* the properties of this segment. For instance, if this segment is <em>immutable</em>
|
||||
@ -322,8 +398,8 @@ public interface MemorySegment extends AutoCloseable {
|
||||
* buffer. The segment starts relative to the buffer's position (inclusive)
|
||||
* and ends relative to the buffer's limit (exclusive).
|
||||
* <p>
|
||||
* The segment will feature all <a href="#access-modes">access modes</a>, unless the given
|
||||
* buffer is {@linkplain ByteBuffer#isReadOnly() read-only} in which case the segment will
|
||||
* The segment will feature all <a href="#access-modes">access modes</a> (see {@link #ALL_ACCESS}),
|
||||
* unless the given buffer is {@linkplain ByteBuffer#isReadOnly() read-only} in which case the segment will
|
||||
* not feature the {@link #WRITE} access mode.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing buffer, to ensure it remains <em>reachable</em>
|
||||
@ -340,7 +416,8 @@ public interface MemorySegment extends AutoCloseable {
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated byte array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
@ -353,7 +430,8 @@ public interface MemorySegment extends AutoCloseable {
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated char array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
@ -366,7 +444,8 @@ public interface MemorySegment extends AutoCloseable {
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated short array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
@ -392,7 +471,8 @@ public interface MemorySegment extends AutoCloseable {
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated float array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
@ -405,7 +485,8 @@ public interface MemorySegment extends AutoCloseable {
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated long array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
@ -418,7 +499,8 @@ public interface MemorySegment extends AutoCloseable {
|
||||
* Creates a new array memory segment that models the memory associated with a given heap-allocated double array.
|
||||
* <p>
|
||||
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
*
|
||||
* @param arr the primitive array backing the array memory segment.
|
||||
* @return a new array memory segment.
|
||||
@ -470,29 +552,32 @@ allocateNative(bytesSize, 1);
|
||||
/**
|
||||
* Creates a new mapped memory segment that models a memory-mapped region of a file from a given path.
|
||||
* <p>
|
||||
* The segment will feature all <a href="#access-modes">access modes</a>, unless the given mapping mode
|
||||
* is {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, in which case the segment will not feature
|
||||
* the {@link #WRITE} access mode.
|
||||
* The segment will feature all <a href="#access-modes">access modes</a> (see {@link #ALL_ACCESS}),
|
||||
* unless the given mapping mode is {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, in which case
|
||||
* the segment will not feature the {@link #WRITE} access mode.
|
||||
*
|
||||
* @implNote When obtaining a mapped segment from a newly created file, the initialization state of the contents of the block
|
||||
* of mapped memory associated with the returned mapped memory segment is unspecified and should not be relied upon.
|
||||
*
|
||||
* @param path the path to the file to memory map.
|
||||
* @param bytesOffset the offset (expressed in bytes) within the file at which the mapped segment is to start.
|
||||
* @param bytesSize the size (in bytes) of the mapped memory backing the memory segment.
|
||||
* @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}; the chosen mapping mode
|
||||
* might affect the behavior of the returned memory mapped segment (see {@link MappedMemorySegment#force()}).
|
||||
* @return a new mapped memory segment.
|
||||
* @throws IllegalArgumentException if {@code bytesOffset < 0}.
|
||||
* @throws IllegalArgumentException if {@code bytesSize < 0}.
|
||||
* @throws UnsupportedOperationException if an unsupported map mode is specified.
|
||||
* @throws IOException if the specified path does not point to an existing file, or if some other I/O error occurs.
|
||||
*/
|
||||
static MappedMemorySegment mapFromPath(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
|
||||
return MappedMemorySegmentImpl.makeMappedSegment(path, bytesSize, mapMode);
|
||||
static MappedMemorySegment mapFromPath(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
|
||||
return MappedMemorySegmentImpl.makeMappedSegment(path, bytesOffset, bytesSize, mapMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size and
|
||||
* alignment constraint (in bytes). The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* alignment constraint (in bytes). The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
*
|
||||
* @implNote The block of off-heap memory associated with the returned native memory segment is initialized to zero.
|
||||
* Moreover, a client is responsible to call the {@link MemorySegment#close()} on a native memory segment,
|
||||
@ -522,7 +607,8 @@ allocateNative(bytesSize, 1);
|
||||
* bounds, and can therefore be closed; closing such a segment can optionally result in calling an user-provided cleanup
|
||||
* action. This method can be very useful when interacting with custom native memory sources (e.g. custom allocators,
|
||||
* GPU memory, etc.), where an address to some underlying memory region is typically obtained from native code
|
||||
* (often as a plain {@code long} value). The segment will feature all <a href="#access-modes">access modes</a>.
|
||||
* (often as a plain {@code long} value). The segment will feature all <a href="#access-modes">access modes</a>
|
||||
* (see {@link #ALL_ACCESS}).
|
||||
* <p>
|
||||
* This method is <em>restricted</em>. Restricted method are unsafe, and, if used incorrectly, their use might crash
|
||||
* the JVM crash or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
@ -590,4 +676,11 @@ allocateNative(bytesSize, 1);
|
||||
* @see MemorySegment#withAccessModes(int)
|
||||
*/
|
||||
int HANDOFF = ACQUIRE << 1;
|
||||
|
||||
/**
|
||||
* Default access mode; this is a union of all the access modes supported by memory segments.
|
||||
* @see MemorySegment#accessModes()
|
||||
* @see MemorySegment#withAccessModes(int)
|
||||
*/
|
||||
int ALL_ACCESS = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.MemorySegmentProxy;
|
||||
import jdk.internal.access.foreign.UnmapperProxy;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
@ -57,14 +59,14 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public abstract class AbstractMemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
|
||||
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
private static final boolean enableSmallSegments =
|
||||
Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.SmallSegments", "true"));
|
||||
|
||||
final static int ACCESS_MASK = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
|
||||
final static int FIRST_RESERVED_FLAG = 1 << 16; // upper 16 bits are reserved
|
||||
final static int SMALL = FIRST_RESERVED_FLAG;
|
||||
final static long NONCE = new Random().nextLong();
|
||||
final static int DEFAULT_MASK = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
|
||||
|
||||
final static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
|
||||
|
||||
@ -89,8 +91,8 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
|
||||
|
||||
static int defaultAccessModes(long size) {
|
||||
return (enableSmallSegments && size < Integer.MAX_VALUE) ?
|
||||
DEFAULT_MASK | SMALL :
|
||||
DEFAULT_MASK;
|
||||
ALL_ACCESS | SMALL :
|
||||
ALL_ACCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -113,6 +115,59 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
|
||||
(AbstractMemorySegmentImpl)segment.withAccessModes(segment.accessModes() & ~CLOSE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MemorySegment fill(byte value){
|
||||
checkRange(0, length, true);
|
||||
UNSAFE.setMemory(base(), min(), length, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void copyFrom(MemorySegment src) {
|
||||
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)src;
|
||||
long size = that.byteSize();
|
||||
checkRange(0, size, true);
|
||||
that.checkRange(0, size, false);
|
||||
UNSAFE.copyMemory(
|
||||
that.base(), that.min(),
|
||||
base(), min(), size);
|
||||
}
|
||||
|
||||
private final static VarHandle BYTE_HANDLE = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
|
||||
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
|
||||
|
||||
@Override
|
||||
public long mismatch(MemorySegment other) {
|
||||
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)other;
|
||||
final long thisSize = this.byteSize();
|
||||
final long thatSize = that.byteSize();
|
||||
final long length = Math.min(thisSize, thatSize);
|
||||
this.checkRange(0, length, false);
|
||||
that.checkRange(0, length, false);
|
||||
if (this == other) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
long i = 0;
|
||||
if (length > 7) {
|
||||
i = ArraysSupport.vectorizedMismatchLarge(
|
||||
this.base(), this.min(),
|
||||
that.base(), that.min(),
|
||||
length, ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE);
|
||||
if (i >= 0) {
|
||||
return i;
|
||||
}
|
||||
i = length - ~i;
|
||||
}
|
||||
MemoryAddress thisAddress = this.baseAddress();
|
||||
MemoryAddress thatAddress = that.baseAddress();
|
||||
for (; i < length; i++) {
|
||||
if ((byte) BYTE_HANDLE.get(thisAddress, i) != (byte) BYTE_HANDLE.get(thatAddress, i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return thisSize != thatSize ? length : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public final MemoryAddress baseAddress() {
|
||||
@ -135,7 +190,7 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
|
||||
|
||||
@Override
|
||||
public final int accessModes() {
|
||||
return mask & ACCESS_MASK;
|
||||
return mask & ALL_ACCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -159,7 +214,7 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
|
||||
if ((~accessModes() & accessModes) != 0) {
|
||||
throw new IllegalArgumentException("Cannot acquire more access modes");
|
||||
}
|
||||
return dup(0, length, (mask & ~ACCESS_MASK) | accessModes, scope);
|
||||
return dup(0, length, (mask & ~ALL_ACCESS) | accessModes, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -169,7 +224,7 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
|
||||
}
|
||||
|
||||
private void checkAccessModes(int accessModes) {
|
||||
if ((accessModes & ~ACCESS_MASK) != 0) {
|
||||
if ((accessModes & ~ALL_ACCESS) != 0) {
|
||||
throw new IllegalArgumentException("Invalid access modes");
|
||||
}
|
||||
}
|
||||
@ -216,7 +271,7 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
|
||||
checkIntSize("byte[]");
|
||||
byte[] arr = new byte[(int)length];
|
||||
MemorySegment arrSegment = MemorySegment.ofArray(arr);
|
||||
MemoryAddress.copy(baseAddress(), arrSegment.baseAddress(), length);
|
||||
arrSegment.copyFrom(this);
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
@ -99,10 +99,11 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl implements
|
||||
|
||||
// factories
|
||||
|
||||
public static MappedMemorySegment makeMappedSegment(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
|
||||
if (bytesSize <= 0) throw new IllegalArgumentException("Requested bytes size must be > 0.");
|
||||
public static MappedMemorySegment makeMappedSegment(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
|
||||
if (bytesSize < 0) throw new IllegalArgumentException("Requested bytes size must be >= 0.");
|
||||
if (bytesOffset < 0) throw new IllegalArgumentException("Requested bytes offset must be >= 0.");
|
||||
try (FileChannelImpl channelImpl = (FileChannelImpl)FileChannel.open(path, openOptions(mapMode))) {
|
||||
UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, 0L, bytesSize);
|
||||
UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, bytesOffset, bytesSize);
|
||||
MemoryScope scope = MemoryScope.create(null, unmapperProxy::unmap);
|
||||
int modes = defaultAccessModes(bytesSize);
|
||||
if (mapMode == FileChannel.MapMode.READ_ONLY) {
|
||||
|
@ -49,6 +49,9 @@ public class TestAdaptVarHandles {
|
||||
|
||||
static MethodHandle S2I;
|
||||
static MethodHandle I2S;
|
||||
static MethodHandle CTX_I2S;
|
||||
static MethodHandle O2I;
|
||||
static MethodHandle I2O;
|
||||
static MethodHandle S2L;
|
||||
static MethodHandle S2L_EX;
|
||||
static MethodHandle S2I_EX;
|
||||
@ -61,6 +64,10 @@ public class TestAdaptVarHandles {
|
||||
try {
|
||||
S2I = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToInt", MethodType.methodType(int.class, String.class));
|
||||
I2S = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "intToString", MethodType.methodType(String.class, int.class));
|
||||
CTX_I2S = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "ctxIntToString",
|
||||
MethodType.methodType(String.class, String.class, String.class, int.class));
|
||||
O2I = MethodHandles.explicitCastArguments(S2I, MethodType.methodType(int.class, Object.class));
|
||||
I2O = MethodHandles.explicitCastArguments(I2S, MethodType.methodType(Object.class, int.class));
|
||||
S2L = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLong", MethodType.methodType(long.class, String.class));
|
||||
S2L_EX = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLongException", MethodType.methodType(long.class, String.class));
|
||||
BASE_ADDR = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "baseAddress", MethodType.methodType(MemoryAddress.class, MemorySegment.class));
|
||||
@ -98,6 +105,46 @@ public class TestAdaptVarHandles {
|
||||
assertEquals(value, "42");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterValueComposite() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout);
|
||||
VarHandle intHandle = layout.varHandle(int.class);
|
||||
MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class);
|
||||
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, CTX_S2I, CTX_I2S);
|
||||
i2SHandle = MemoryHandles.insertCoordinates(i2SHandle, 1, "a", "b");
|
||||
i2SHandle.set(segment.baseAddress(), "1");
|
||||
String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42");
|
||||
assertEquals(oldValue, "ab1");
|
||||
String value = (String)i2SHandle.get(segment.baseAddress());
|
||||
assertEquals(value, "ab43");
|
||||
boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12");
|
||||
assertTrue(swapped);
|
||||
oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42");
|
||||
assertEquals(oldValue, "ab12");
|
||||
value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress());
|
||||
assertEquals(value, "ab42");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterValueLoose() throws Throwable {
|
||||
ValueLayout layout = MemoryLayouts.JAVA_INT;
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout);
|
||||
VarHandle intHandle = layout.varHandle(int.class);
|
||||
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, O2I, I2O);
|
||||
i2SHandle.set(segment.baseAddress(), "1");
|
||||
String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42");
|
||||
assertEquals(oldValue, "1");
|
||||
String value = (String)i2SHandle.get(segment.baseAddress());
|
||||
assertEquals(value, "43");
|
||||
boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12");
|
||||
assertTrue(swapped);
|
||||
oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42");
|
||||
assertEquals(oldValue, "12");
|
||||
value = (String)(Object)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress());
|
||||
assertEquals(value, "42");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testBadFilterNullTarget() {
|
||||
MemoryHandles.filterValue(null, S2I, I2S);
|
||||
@ -133,6 +180,14 @@ public class TestAdaptVarHandles {
|
||||
MemoryHandles.filterValue(intHandle, S2I, I2S.bindTo(42));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadFilterBoxPrefixCoordinates() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
MemoryHandles.filterValue(intHandle,
|
||||
MethodHandles.dropArguments(S2I, 1, int.class),
|
||||
MethodHandles.dropArguments(I2S, 1, long.class));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadFilterBoxException() {
|
||||
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
|
||||
@ -461,4 +516,8 @@ public class TestAdaptVarHandles {
|
||||
}
|
||||
|
||||
static void void_filter(String s) { }
|
||||
|
||||
static String ctxIntToString(String a, String b, int i) {
|
||||
return a + b + String.valueOf(i);
|
||||
}
|
||||
}
|
||||
|
@ -143,17 +143,20 @@ public class TestByteBuffer {
|
||||
static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement());
|
||||
|
||||
|
||||
static void initTuples(MemoryAddress base) {
|
||||
for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
|
||||
static void initTuples(MemoryAddress base, long count) {
|
||||
for (long i = 0; i < count ; i++) {
|
||||
indexHandle.set(base, i, (int)i);
|
||||
valueHandle.set(base, i, (float)(i / 500f));
|
||||
}
|
||||
}
|
||||
|
||||
static void checkTuples(MemoryAddress base, ByteBuffer bb) {
|
||||
for (long i = 0; i < tuples.elementCount().getAsLong() ; i++) {
|
||||
assertEquals(bb.getInt(), (int)indexHandle.get(base, i));
|
||||
assertEquals(bb.getFloat(), (float)valueHandle.get(base, i));
|
||||
static void checkTuples(MemoryAddress base, ByteBuffer bb, long count) {
|
||||
for (long i = 0; i < count ; i++) {
|
||||
int index;
|
||||
float value;
|
||||
assertEquals(index = bb.getInt(), (int)indexHandle.get(base, i));
|
||||
assertEquals(value = bb.getFloat(), (float)valueHandle.get(base, i));
|
||||
assertEquals(value, index / 500f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,10 +195,10 @@ public class TestByteBuffer {
|
||||
public void testOffheap() {
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(tuples)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initTuples(base);
|
||||
initTuples(base, tuples.elementCount().getAsLong());
|
||||
|
||||
ByteBuffer bb = segment.asByteBuffer();
|
||||
checkTuples(base, bb);
|
||||
checkTuples(base, bb, tuples.elementCount().getAsLong());
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,10 +207,10 @@ public class TestByteBuffer {
|
||||
byte[] arr = new byte[(int) tuples.byteSize()];
|
||||
MemorySegment region = MemorySegment.ofArray(arr);
|
||||
MemoryAddress base = region.baseAddress();
|
||||
initTuples(base);
|
||||
initTuples(base, tuples.elementCount().getAsLong());
|
||||
|
||||
ByteBuffer bb = region.asByteBuffer();
|
||||
checkTuples(base, bb);
|
||||
checkTuples(base, bb, tuples.elementCount().getAsLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -221,7 +224,7 @@ public class TestByteBuffer {
|
||||
withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
|
||||
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initTuples(base);
|
||||
initTuples(base, tuples.elementCount().getAsLong());
|
||||
mbb.force();
|
||||
});
|
||||
}
|
||||
@ -231,23 +234,21 @@ public class TestByteBuffer {
|
||||
withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {
|
||||
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
checkTuples(base, mbb);
|
||||
checkTuples(base, mbb, tuples.elementCount().getAsLong());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
|
||||
|
||||
@Test
|
||||
public void testDefaultAccessModesMappedSegment() throws Throwable {
|
||||
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 8, FileChannel.MapMode.READ_WRITE)) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
|
||||
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 0L, 8, FileChannel.MapMode.READ_WRITE)) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS);
|
||||
}
|
||||
|
||||
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 8, FileChannel.MapMode.READ_ONLY)) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES & ~WRITE));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS_MODES& ~WRITE);
|
||||
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 0L, 8, FileChannel.MapMode.READ_ONLY)) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS & ~WRITE));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS & ~WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,16 +259,44 @@ public class TestByteBuffer {
|
||||
f.deleteOnExit();
|
||||
|
||||
//write to channel
|
||||
try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
|
||||
try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initTuples(base);
|
||||
initTuples(base, tuples.elementCount().getAsLong());
|
||||
segment.force();
|
||||
}
|
||||
|
||||
//read from channel
|
||||
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
|
||||
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
checkTuples(base, segment.asByteBuffer());
|
||||
checkTuples(base, segment.asByteBuffer(), tuples.elementCount().getAsLong());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappedSegmentOffset() throws Throwable {
|
||||
File f = new File("test3.out");
|
||||
f.createNewFile();
|
||||
f.deleteOnExit();
|
||||
|
||||
MemoryLayout tupleLayout = tuples.elementLayout();
|
||||
|
||||
// write one at a time
|
||||
for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
|
||||
//write to channel
|
||||
try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), i, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
initTuples(base, 1);
|
||||
segment.force();
|
||||
}
|
||||
}
|
||||
|
||||
// check one at a time
|
||||
for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
|
||||
//read from channel
|
||||
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
|
||||
MemoryAddress base = segment.baseAddress();
|
||||
checkTuples(base, segment.asByteBuffer(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,6 +466,31 @@ public class TestByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadMapNegativeSize() throws IOException {
|
||||
File f = new File("testNeg1.out");
|
||||
f.createNewFile();
|
||||
f.deleteOnExit();
|
||||
MemorySegment.mapFromPath(f.toPath(), 0L, -1, FileChannel.MapMode.READ_WRITE);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadMapNegativeOffset() throws IOException {
|
||||
File f = new File("testNeg2.out");
|
||||
f.createNewFile();
|
||||
f.deleteOnExit();
|
||||
MemorySegment.mapFromPath(f.toPath(), -1, 1, FileChannel.MapMode.READ_WRITE);
|
||||
}
|
||||
|
||||
public void testMapZeroSize() throws IOException {
|
||||
File f = new File("testPos1.out");
|
||||
f.createNewFile();
|
||||
f.deleteOnExit();
|
||||
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_WRITE)) {
|
||||
assertEquals(segment.byteSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider="resizeOps")
|
||||
public void testCopyHeapToNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
|
||||
checkByteArrayAlignment(seq.elementLayout());
|
||||
@ -444,7 +498,7 @@ public class TestByteBuffer {
|
||||
try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes);
|
||||
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
|
||||
initializer.accept(heapArray.baseAddress());
|
||||
MemoryAddress.copy(heapArray.baseAddress(), nativeArray.baseAddress(), bytes);
|
||||
nativeArray.copyFrom(heapArray);
|
||||
checker.accept(nativeArray.baseAddress());
|
||||
}
|
||||
}
|
||||
@ -456,7 +510,7 @@ public class TestByteBuffer {
|
||||
try (MemorySegment nativeArray = MemorySegment.allocateNative(seq);
|
||||
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
|
||||
initializer.accept(nativeArray.baseAddress());
|
||||
MemoryAddress.copy(nativeArray.baseAddress(), heapArray.baseAddress(), bytes);
|
||||
heapArray.copyFrom(nativeArray);
|
||||
checker.accept(heapArray.baseAddress());
|
||||
}
|
||||
}
|
||||
@ -465,14 +519,14 @@ public class TestByteBuffer {
|
||||
public void testDefaultAccessModesOfBuffer() {
|
||||
ByteBuffer rwBuffer = ByteBuffer.wrap(new byte[4]);
|
||||
try (MemorySegment segment = MemorySegment.ofByteBuffer(rwBuffer)) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS);
|
||||
}
|
||||
|
||||
ByteBuffer roBuffer = rwBuffer.asReadOnlyBuffer();
|
||||
try (MemorySegment segment = MemorySegment.ofByteBuffer(roBuffer)) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES & ~WRITE));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS_MODES & ~WRITE);
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS & ~WRITE));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS & ~WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,64 +36,126 @@ import jdk.incubator.foreign.SequenceLayout;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestLayoutPaths {
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadSelectFromSeq() {
|
||||
public void testBadBitSelectFromSeq() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.groupElement("foo"));
|
||||
seq.bitOffset(PathElement.groupElement("foo"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadSelectFromStruct() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.offset(PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadSelectFromValue() {
|
||||
public void testBadByteSelectFromSeq() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(), PathElement.sequenceElement());
|
||||
seq.byteOffset(PathElement.groupElement("foo"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testUnknownStructField() {
|
||||
public void testBadBitSelectFromStruct() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.offset(PathElement.groupElement("foo"));
|
||||
g.bitOffset(PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadByteSelectFromStruct() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.byteOffset(PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadBitSelectFromValue() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
|
||||
seq.bitOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBadByteSelectFromValue() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT);
|
||||
seq.byteOffset(PathElement.sequenceElement(), PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testUnknownBitStructField() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.bitOffset(PathElement.groupElement("foo"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testUnknownByteStructField() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.byteOffset(PathElement.groupElement("foo"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNullGroupElementName() {
|
||||
PathElement.groupElement(null);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testBitNullGroupElementName() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.offset(PathElement.groupElement(null));
|
||||
g.bitOffset(PathElement.groupElement(null));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testByteNullGroupElementName() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT);
|
||||
g.byteOffset(PathElement.groupElement(null));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testOutOfBoundsSeqIndex() {
|
||||
public void testBitOutOfBoundsSeqIndex() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(6));
|
||||
seq.bitOffset(PathElement.sequenceElement(6));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testByteOutOfBoundsSeqIndex() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.byteOffset(PathElement.sequenceElement(6));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNegativeSeqIndex() {
|
||||
PathElement.sequenceElement(-2);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBitNegativeSeqIndex() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(-2));
|
||||
seq.bitOffset(PathElement.sequenceElement(-2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testByteNegativeSeqIndex() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.byteOffset(PathElement.sequenceElement(-2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testOutOfBoundsSeqRange() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(6, 2));
|
||||
seq.bitOffset(PathElement.sequenceElement(6, 2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNegativeSeqRange() {
|
||||
PathElement.sequenceElement(-2, 2);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBitNegativeSeqRange() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.offset(PathElement.sequenceElement(-2, 2));
|
||||
seq.bitOffset(PathElement.sequenceElement(-2, 2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testByteNegativeSeqRange() {
|
||||
SequenceLayout seq = MemoryLayout.ofSequence(5, MemoryLayouts.JAVA_INT);
|
||||
seq.byteOffset(PathElement.sequenceElement(-2, 2));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
@ -102,11 +164,18 @@ public class TestLayoutPaths {
|
||||
seq.varHandle(int.class, PathElement.sequenceElement());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = UnsupportedOperationException.class)
|
||||
public void testBadMultiple() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayout.ofPaddingBits(3), MemoryLayouts.JAVA_INT.withName("foo"));
|
||||
g.byteOffset(PathElement.groupElement("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadContainerAlign() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo")).withBitAlignment(8);
|
||||
try {
|
||||
g.offset(PathElement.groupElement("foo"));
|
||||
g.bitOffset(PathElement.groupElement("foo"));
|
||||
g.byteOffset(PathElement.groupElement("foo"));
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex); // should be ok!
|
||||
}
|
||||
@ -124,7 +193,8 @@ public class TestLayoutPaths {
|
||||
public void testBadAlignOffset() {
|
||||
GroupLayout g = MemoryLayout.ofStruct(MemoryLayouts.PAD_8, MemoryLayouts.JAVA_INT.withBitAlignment(16).withName("foo"));
|
||||
try {
|
||||
g.offset(PathElement.groupElement("foo"));
|
||||
g.bitOffset(PathElement.groupElement("foo"));
|
||||
g.byteOffset(PathElement.groupElement("foo"));
|
||||
} catch (Throwable ex) {
|
||||
throw new AssertionError(ex); // should be ok!
|
||||
}
|
||||
@ -144,7 +214,13 @@ public class TestLayoutPaths {
|
||||
// bad path elements
|
||||
for (PathElement e : List.of( PathElement.sequenceElement(), PathElement.sequenceElement(0, 2) )) {
|
||||
try {
|
||||
seq.offset(e);
|
||||
seq.bitOffset(e);
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertTrue(true);
|
||||
}
|
||||
try {
|
||||
seq.byteOffset(e);
|
||||
fail();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertTrue(true);
|
||||
@ -198,8 +274,10 @@ public class TestLayoutPaths {
|
||||
// test offset
|
||||
|
||||
for (int i = 1 ; i <= 4 ; i++) {
|
||||
long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
|
||||
assertEquals(offsets[i - 1], offset);
|
||||
long bitOffset = g.bitOffset(PathElement.groupElement(String.valueOf(i)));
|
||||
assertEquals(offsets[i - 1], bitOffset);
|
||||
long byteOffset = g.byteOffset(PathElement.groupElement(String.valueOf(i)));
|
||||
assertEquals((offsets[i - 1]) >>> 3, byteOffset);
|
||||
}
|
||||
|
||||
// test map
|
||||
@ -237,8 +315,10 @@ public class TestLayoutPaths {
|
||||
// test offset
|
||||
|
||||
for (int i = 1 ; i <= 4 ; i++) {
|
||||
long offset = g.offset(PathElement.groupElement(String.valueOf(i)));
|
||||
assertEquals(offsets[i - 1], offset);
|
||||
long bitOffset = g.bitOffset(PathElement.groupElement(String.valueOf(i)));
|
||||
assertEquals(offsets[i - 1], bitOffset);
|
||||
long byteOffset = g.byteOffset(PathElement.groupElement(String.valueOf(i)));
|
||||
assertEquals((offsets[i - 1]) >>> 3, byteOffset);
|
||||
}
|
||||
|
||||
// test map
|
||||
@ -269,8 +349,10 @@ public class TestLayoutPaths {
|
||||
// test offset
|
||||
|
||||
for (int i = 0 ; i < 4 ; i++) {
|
||||
long offset = g.offset(PathElement.sequenceElement(i));
|
||||
assertEquals(offsets[i], offset);
|
||||
long bitOffset = g.bitOffset(PathElement.sequenceElement(i));
|
||||
assertEquals(offsets[i], bitOffset);
|
||||
long byteOffset = g.byteOffset(PathElement.sequenceElement(i));
|
||||
assertEquals((offsets[i]) >>> 3, byteOffset);
|
||||
}
|
||||
|
||||
// test map
|
||||
|
@ -57,7 +57,7 @@ public class TestMemoryCopy {
|
||||
BYTE_HANDLE.set(addr1.addOffset(i), (byte) i);
|
||||
}
|
||||
//perform copy
|
||||
MemoryAddress.copy(addr1, addr2, size);
|
||||
s2.segment.copyFrom(s1.segment.asSlice(0, size));
|
||||
//check that copy actually worked
|
||||
for (int i = 0 ; i < size ; i++) {
|
||||
assertEquals((byte)i, BYTE_HANDLE.get(addr2.addOffset(i)));
|
||||
|
251
test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java
Normal file
251
test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayout.PathElement;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import org.testng.annotations.*;
|
||||
import static java.nio.ByteOrder.BIG_ENDIAN;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestMemoryHandleAsUnsigned
|
||||
*/
|
||||
|
||||
public class TestMemoryHandleAsUnsigned {
|
||||
|
||||
@DataProvider(name = "unsignedIntToByteData")
|
||||
public Object[][] unsignedIntToByteData() {
|
||||
return IntStream.range(0, 256)
|
||||
.mapToObj(v -> new Object[] { v }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unsignedIntToByteData")
|
||||
public void testUnsignedIntToByte(int intValue) {
|
||||
byte byteValue = (byte) (intValue & 0xFF);
|
||||
|
||||
MemoryLayout layout = MemoryLayouts.BITS_8_BE;
|
||||
VarHandle byteHandle = layout.varHandle(byte.class);
|
||||
VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class);
|
||||
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
intHandle.set(segment.baseAddress(), intValue);
|
||||
int expectedIntValue = Byte.toUnsignedInt(byteValue);
|
||||
assertEquals((int) intHandle.get(segment.baseAddress()), expectedIntValue);
|
||||
assertEquals((byte) byteHandle.get(segment.baseAddress()), byteValue);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "unsignedLongToByteData")
|
||||
public Object[][] unsignedLongToByteData() {
|
||||
return LongStream.range(0L, 256L)
|
||||
.mapToObj(v -> new Object[] { v }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unsignedLongToByteData")
|
||||
public void testUnsignedLongToByte(long longValue) {
|
||||
byte byteValue = (byte) (longValue & 0xFFL);
|
||||
|
||||
MemoryLayout layout = MemoryLayouts.BITS_8_BE;
|
||||
VarHandle byteHandle = layout.varHandle(byte.class);
|
||||
VarHandle longHandle = MemoryHandles.asUnsigned(byteHandle, long.class);
|
||||
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
longHandle.set(segment.baseAddress(), longValue);
|
||||
long expectedLongValue = Byte.toUnsignedLong(byteValue);
|
||||
assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue);
|
||||
assertEquals((byte) byteHandle.get(segment.baseAddress()), byteValue);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "unsignedIntToShortData")
|
||||
public Object[][] unsignedIntToShortData() {
|
||||
return IntStream.range(0, 65_536).filter(i -> i % 99 == 0)
|
||||
.mapToObj(v -> new Object[] { v }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unsignedIntToShortData")
|
||||
public void testUnsignedIntToShort(int intValue) {
|
||||
short shortValue = (short) (intValue & 0xFFFF);
|
||||
|
||||
MemoryLayout layout = MemoryLayouts.BITS_16_BE;
|
||||
VarHandle shortHandle = layout.varHandle(short.class);
|
||||
VarHandle intHandle = MemoryHandles.asUnsigned(shortHandle, int.class);
|
||||
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
intHandle.set(segment.baseAddress(), intValue);
|
||||
int expectedIntValue = Short.toUnsignedInt(shortValue);
|
||||
assertEquals((int) intHandle.get(segment.baseAddress()), expectedIntValue);
|
||||
assertEquals((short) shortHandle.get(segment.baseAddress()), shortValue);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "unsignedLongToShortData")
|
||||
public Object[][] unsignedLongToShortData() {
|
||||
return LongStream.range(0L, 65_536L).filter(i -> i % 99 == 0)
|
||||
.mapToObj(v -> new Object[] { v }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unsignedLongToShortData")
|
||||
public void testUnsignedLongToShort(long longValue) {
|
||||
short shortValue = (short) (longValue & 0xFFFFL);
|
||||
|
||||
MemoryLayout layout = MemoryLayouts.BITS_16_BE;
|
||||
VarHandle shortHandle = layout.varHandle(short.class);
|
||||
VarHandle longHandle = MemoryHandles.asUnsigned(shortHandle, long.class);
|
||||
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
longHandle.set(segment.baseAddress(), longValue);
|
||||
long expectedLongValue = Short.toUnsignedLong(shortValue);
|
||||
assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue);
|
||||
assertEquals((short) shortHandle.get(segment.baseAddress()), shortValue);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "unsignedLongToIntData")
|
||||
public Object[][] unsignedLongToIntData() {
|
||||
// some boundary values
|
||||
long[] l = new long[] { Long.MAX_VALUE, Long.MIN_VALUE,
|
||||
Short.MAX_VALUE - 1L, Short.MAX_VALUE, Short.MAX_VALUE + 1L,
|
||||
Short.MIN_VALUE - 1L, Short.MIN_VALUE, Short.MIN_VALUE + 1L, };
|
||||
return LongStream.concat(LongStream.range(-256L, 256L), Arrays.stream(l))
|
||||
.mapToObj(v -> new Object[] { v }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "unsignedLongToIntData")
|
||||
public void testUnsignedLongToInt(long longValue) {
|
||||
int intValue = (int) (longValue & 0xFFFF_FFFFL);
|
||||
|
||||
MemoryLayout layout = MemoryLayouts.BITS_32_BE;
|
||||
VarHandle intHandle = layout.varHandle(int.class);
|
||||
VarHandle longHandle = MemoryHandles.asUnsigned(intHandle, long.class);
|
||||
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
longHandle.set(segment.baseAddress(), longValue);
|
||||
long expectedLongValue = Integer.toUnsignedLong(intValue);
|
||||
assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue);
|
||||
assertEquals((int) intHandle.get(segment.baseAddress()), intValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoordinatesSequenceLayout() {
|
||||
MemoryLayout layout = MemoryLayout.ofSequence(2, MemoryLayouts.BITS_8_BE);
|
||||
VarHandle byteHandle = layout.varHandle(byte.class, PathElement.sequenceElement());
|
||||
VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class);
|
||||
|
||||
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
|
||||
intHandle.set(segment.baseAddress(), 0L, (int) -1);
|
||||
assertEquals((int) intHandle.get(segment.baseAddress(), 0L), 255);
|
||||
intHandle.set(segment.baseAddress(), 1L, (int) 200);
|
||||
assertEquals((int) intHandle.get(segment.baseAddress(), 1L), 200);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoordinatesStride() {
|
||||
byte[] arr = { 0, 0, (byte) 129, 0 };
|
||||
MemorySegment segment = MemorySegment.ofArray(arr);
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
|
||||
{
|
||||
VarHandle byteHandle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
|
||||
VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class);
|
||||
VarHandle strideHandle = MemoryHandles.withStride(intHandle, 1);
|
||||
assertEquals((int) strideHandle.get(addr, 2L), 129);
|
||||
}
|
||||
{
|
||||
VarHandle byteHandle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
|
||||
VarHandle strideHandle = MemoryHandles.withStride(byteHandle, 1);
|
||||
VarHandle intHandle = MemoryHandles.asUnsigned(strideHandle, int.class);
|
||||
assertEquals((int) intHandle.get(addr, 2L), 129);
|
||||
}
|
||||
}
|
||||
|
||||
static final Class<NullPointerException> NPE = NullPointerException.class;
|
||||
|
||||
@Test
|
||||
public void testNull() {
|
||||
VarHandle handle = MemoryHandles.varHandle(byte.class, BIG_ENDIAN);
|
||||
assertThrows(NPE, () -> MemoryHandles.asUnsigned(handle, null));
|
||||
assertThrows(NPE, () -> MemoryHandles.asUnsigned(null, short.class));
|
||||
assertThrows(NPE, () -> MemoryHandles.asUnsigned(null, null));
|
||||
}
|
||||
|
||||
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
|
||||
|
||||
static void assertIllegalArgumentExceptionIllegalCarrier(Class<?> carrier, Class<?> adaptedType) {
|
||||
var vh = MemoryHandles.varHandle(carrier, BIG_ENDIAN);
|
||||
var exception = expectThrows(IAE, () -> MemoryHandles.asUnsigned(vh, adaptedType));
|
||||
var msg = exception.getMessage();
|
||||
assertTrue(msg.contains("illegal carrier"), "Expected \"illegal carrier\" in:[" + msg +"]");
|
||||
}
|
||||
|
||||
static void assertIllegalArgumentExceptionIllegalAdapter(Class<?> carrier, Class<?> adaptedType) {
|
||||
var vh = MemoryHandles.varHandle(carrier, BIG_ENDIAN);
|
||||
var exception = expectThrows(IAE, () -> MemoryHandles.asUnsigned(vh, adaptedType));
|
||||
var msg = exception.getMessage();
|
||||
assertTrue(msg.contains("illegal adapter type"), "Expected \"illegal adapter type\" in:[" + msg +"]");
|
||||
}
|
||||
|
||||
static void assertIllegalArgumentExceptionIsNotWiderThan(Class<?> carrier, Class<?> adaptedType) {
|
||||
var vh = MemoryHandles.varHandle(carrier, BIG_ENDIAN);
|
||||
var exception = expectThrows(IAE, () -> MemoryHandles.asUnsigned(vh, adaptedType));
|
||||
var msg = exception.getMessage();
|
||||
assertTrue(msg.contains("is not wider than"), "Expected \"is not wider than\" in:[" + msg +"]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalArgumentException() {
|
||||
assertIllegalArgumentExceptionIllegalCarrier(char.class, long.class);
|
||||
assertIllegalArgumentExceptionIllegalCarrier(double.class, long.class);
|
||||
assertIllegalArgumentExceptionIllegalCarrier(float.class, long.class);
|
||||
assertIllegalArgumentExceptionIllegalCarrier(long.class, long.class);
|
||||
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, void.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, byte.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, short.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, char.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, double.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, float.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, Object.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, Integer.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, Long.class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, long[].class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, int[].class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, Integer[].class);
|
||||
assertIllegalArgumentExceptionIllegalAdapter(byte.class, Long[].class);
|
||||
|
||||
assertIllegalArgumentExceptionIsNotWiderThan(int.class, int.class);
|
||||
}
|
||||
}
|
242
test/jdk/java/foreign/TestMismatch.java
Normal file
242
test/jdk/java/foreign/TestMismatch.java
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng TestMismatch
|
||||
*/
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.IntFunction;
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static java.lang.System.out;
|
||||
import static jdk.incubator.foreign.MemorySegment.READ;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
public class TestMismatch {
|
||||
|
||||
final static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class);
|
||||
|
||||
// stores a increasing sequence of values into the memory of the given segment
|
||||
static MemorySegment initializeSegment(MemorySegment segment) {
|
||||
MemoryAddress addr = segment.baseAddress();
|
||||
for (int i = 0 ; i < segment.byteSize() ; i++) {
|
||||
BYTE_HANDLE.set(addr.addOffset(i), (byte)i);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices")
|
||||
public void testSameValues(MemorySegment ss1, MemorySegment ss2) {
|
||||
out.format("testSameValues s1:%s, s2:%s\n", ss1, ss2);
|
||||
MemorySegment s1 = initializeSegment(ss1);
|
||||
MemorySegment s2 = initializeSegment(ss2);
|
||||
|
||||
if (s1.byteSize() == s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), -1); // identical
|
||||
assertEquals(s2.mismatch(s1), -1);
|
||||
} else if (s1.byteSize() > s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), s2.byteSize()); // proper prefix
|
||||
assertEquals(s2.mismatch(s1), s2.byteSize());
|
||||
} else {
|
||||
assert s1.byteSize() < s2.byteSize();
|
||||
assertEquals(s1.mismatch(s2), s1.byteSize()); // proper prefix
|
||||
assertEquals(s2.mismatch(s1), s1.byteSize());
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "slices")
|
||||
public void testDifferentValues(MemorySegment s1, MemorySegment s2) {
|
||||
out.format("testDifferentValues s1:%s, s2:%s\n", s1, s2);
|
||||
s1 = initializeSegment(s1);
|
||||
s2 = initializeSegment(s2);
|
||||
|
||||
for (long i = s2.byteSize() -1 ; i >= 0; i--) {
|
||||
long expectedMismatchOffset = i;
|
||||
BYTE_HANDLE.set(s2.baseAddress().addOffset(i), (byte) 0xFF);
|
||||
|
||||
if (s1.byteSize() == s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
|
||||
assertEquals(s2.mismatch(s1), expectedMismatchOffset);
|
||||
} else if (s1.byteSize() > s2.byteSize()) {
|
||||
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
|
||||
assertEquals(s2.mismatch(s1), expectedMismatchOffset);
|
||||
} else {
|
||||
assert s1.byteSize() < s2.byteSize();
|
||||
var off = Math.min(s1.byteSize(), expectedMismatchOffset);
|
||||
assertEquals(s1.mismatch(s2), off); // proper prefix
|
||||
assertEquals(s2.mismatch(s1), off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty() {
|
||||
var s1 = MemorySegment.ofArray(new byte[0]);
|
||||
assertEquals(s1.mismatch(s1), -1);
|
||||
try (var nativeSegment = MemorySegment.allocateNative(4)) {
|
||||
var s2 = nativeSegment.asSlice(0, 0);
|
||||
assertEquals(s1.mismatch(s2), -1);
|
||||
assertEquals(s2.mismatch(s1), -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLarge() {
|
||||
try (var s1 = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L);
|
||||
var s2 = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L)) {
|
||||
assertEquals(s1.mismatch(s1), -1);
|
||||
assertEquals(s1.mismatch(s2), -1);
|
||||
assertEquals(s2.mismatch(s1), -1);
|
||||
|
||||
for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) {
|
||||
BYTE_HANDLE.set(s2.baseAddress().addOffset(i), (byte) 0xFF);
|
||||
long expectedMismatchOffset = i;
|
||||
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
|
||||
assertEquals(s2.mismatch(s1), expectedMismatchOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final Class<IllegalStateException> ISE = IllegalStateException.class;
|
||||
static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
|
||||
|
||||
@Test
|
||||
public void testClosed() {
|
||||
var s1 = MemorySegment.ofArray(new byte[4]);
|
||||
var s2 = MemorySegment.ofArray(new byte[4]);
|
||||
s1.close();
|
||||
assertThrows(ISE, () -> s1.mismatch(s1));
|
||||
assertThrows(ISE, () -> s1.mismatch(s2));
|
||||
assertThrows(ISE, () -> s2.mismatch(s1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsufficientAccessModes() {
|
||||
var s1 = MemorySegment.ofArray(new byte[4]);
|
||||
var s2 = MemorySegment.ofArray(new byte[4]);
|
||||
var s1WithoutRead = s1.withAccessModes(s1.accessModes() & ~READ);
|
||||
var s2WithoutRead = s2.withAccessModes(s2.accessModes() & ~READ);
|
||||
|
||||
assertThrows(UOE, () -> s1.mismatch(s2WithoutRead));
|
||||
assertThrows(UOE, () -> s1WithoutRead.mismatch(s2));
|
||||
assertThrows(UOE, () -> s1WithoutRead.mismatch(s2WithoutRead));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNull() {
|
||||
var segment = MemorySegment.ofArray(new byte[4]);
|
||||
segment.mismatch(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThreadAccess() throws Exception {
|
||||
var segment = MemorySegment.ofArray(new byte[4]);
|
||||
{
|
||||
AtomicReference<RuntimeException> exception = new AtomicReference<>();
|
||||
Runnable action = () -> {
|
||||
try {
|
||||
MemorySegment.ofArray(new byte[4]).mismatch(segment);
|
||||
} catch (RuntimeException e) {
|
||||
exception.set(e);
|
||||
}
|
||||
};
|
||||
Thread thread = new Thread(action);
|
||||
thread.start();
|
||||
thread.join();
|
||||
|
||||
RuntimeException e = exception.get();
|
||||
if (!(e instanceof IllegalStateException)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
{
|
||||
AtomicReference<RuntimeException> exception = new AtomicReference<>();
|
||||
Runnable action = () -> {
|
||||
try {
|
||||
segment.mismatch(MemorySegment.ofArray(new byte[4]));
|
||||
} catch (RuntimeException e) {
|
||||
exception.set(e);
|
||||
}
|
||||
};
|
||||
Thread thread = new Thread(action);
|
||||
thread.start();
|
||||
thread.join();
|
||||
|
||||
RuntimeException e = exception.get();
|
||||
if (!(e instanceof IllegalStateException)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SegmentKind {
|
||||
NATIVE(MemorySegment::allocateNative),
|
||||
ARRAY(i -> MemorySegment.ofArray(new byte[i]));
|
||||
|
||||
final IntFunction<MemorySegment> segmentFactory;
|
||||
|
||||
SegmentKind(IntFunction<MemorySegment> segmentFactory) {
|
||||
this.segmentFactory = segmentFactory;
|
||||
}
|
||||
|
||||
MemorySegment makeSegment(int elems) {
|
||||
return segmentFactory.apply(elems);
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "slices")
|
||||
static Object[][] slices() {
|
||||
int[] sizes = { 16, 8, 1 };
|
||||
List<MemorySegment> aSlices = new ArrayList<>();
|
||||
List<MemorySegment> bSlices = new ArrayList<>();
|
||||
for (List<MemorySegment> slices : List.of(aSlices, bSlices)) {
|
||||
for (SegmentKind kind : SegmentKind.values()) {
|
||||
MemorySegment segment = kind.makeSegment(16);
|
||||
//compute all slices
|
||||
for (int size : sizes) {
|
||||
for (int index = 0 ; index < 16 ; index += size) {
|
||||
MemorySegment slice = segment.asSlice(index, size);
|
||||
slices.add(slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert aSlices.size() == bSlices.size();
|
||||
Object[][] sliceArray = new Object[aSlices.size() * bSlices.size()][];
|
||||
for (int i = 0 ; i < aSlices.size() ; i++) {
|
||||
for (int j = 0 ; j < bSlices.size() ; j++) {
|
||||
sliceArray[i * aSlices.size() + j] = new Object[] { aSlices.get(i), bSlices.get(j) };
|
||||
}
|
||||
}
|
||||
return sliceArray;
|
||||
}
|
||||
}
|
@ -172,16 +172,14 @@ public class TestNative {
|
||||
}
|
||||
}
|
||||
|
||||
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
|
||||
|
||||
@Test
|
||||
public void testDefaultAccessModes() {
|
||||
MemoryAddress addr = MemoryAddress.ofLong(allocate(12));
|
||||
MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null,
|
||||
() -> free(addr.toRawLongValue()), null);
|
||||
try (MemorySegment segment = mallocSegment) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ import org.testng.annotations.Test;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -145,30 +146,11 @@ public class TestSegments {
|
||||
}
|
||||
}
|
||||
|
||||
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
|
||||
|
||||
@DataProvider(name = "segmentFactories")
|
||||
public Object[][] segmentFactories() {
|
||||
List<Supplier<MemorySegment>> l = List.of(
|
||||
() -> MemorySegment.ofArray(new byte[1]),
|
||||
() -> MemorySegment.ofArray(new char[1]),
|
||||
() -> MemorySegment.ofArray(new double[1]),
|
||||
() -> MemorySegment.ofArray(new float[1]),
|
||||
() -> MemorySegment.ofArray(new int[1]),
|
||||
() -> MemorySegment.ofArray(new long[1]),
|
||||
() -> MemorySegment.ofArray(new short[1]),
|
||||
() -> MemorySegment.ofArray(new int[1]),
|
||||
() -> MemorySegment.allocateNative(1),
|
||||
() -> MemorySegment.allocateNative(1, 2),
|
||||
() -> MemorySegment.allocateNative(MemoryLayout.ofValueBits(8, ByteOrder.LITTLE_ENDIAN))
|
||||
);
|
||||
return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new);
|
||||
}
|
||||
@Test(dataProvider = "segmentFactories")
|
||||
public void testAccessModesOfFactories(Supplier<MemorySegment> memorySegmentSupplier) {
|
||||
try (MemorySegment segment = memorySegmentSupplier.get()) {
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS_MODES));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS_MODES);
|
||||
assertTrue(segment.hasAccessModes(ALL_ACCESS));
|
||||
assertEquals(segment.accessModes(), ALL_ACCESS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,6 +171,93 @@ public class TestSegments {
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "segmentFactories")
|
||||
public Object[][] segmentFactories() {
|
||||
List<Supplier<MemorySegment>> l = List.of(
|
||||
() -> MemorySegment.ofArray(new byte[] { 0x00, 0x01, 0x02, 0x03 }),
|
||||
() -> MemorySegment.ofArray(new char[] {'a', 'b', 'c', 'd' }),
|
||||
() -> MemorySegment.ofArray(new double[] { 1d, 2d, 3d, 4d} ),
|
||||
() -> MemorySegment.ofArray(new float[] { 1.0f, 2.0f, 3.0f, 4.0f }),
|
||||
() -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }),
|
||||
() -> MemorySegment.ofArray(new long[] { 1l, 2l, 3l, 4l } ),
|
||||
() -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ),
|
||||
() -> MemorySegment.allocateNative(4),
|
||||
() -> MemorySegment.allocateNative(4, 8),
|
||||
() -> MemorySegment.allocateNative(MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder()))
|
||||
);
|
||||
return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "segmentFactories")
|
||||
public void testFill(Supplier<MemorySegment> memorySegmentSupplier) {
|
||||
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
|
||||
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
|
||||
|
||||
for (byte value : new byte[] {(byte) 0xFF, (byte) 0x00, (byte) 0x45}) {
|
||||
try (MemorySegment segment = memorySegmentSupplier.get()) {
|
||||
segment.fill(value);
|
||||
for (long l = 0; l < segment.byteSize(); l++) {
|
||||
assertEquals((byte) byteHandle.get(segment.baseAddress(), l), value);
|
||||
}
|
||||
|
||||
// fill a slice
|
||||
var sliceSegment = segment.asSlice(1, segment.byteSize() - 2).fill((byte) ~value);
|
||||
for (long l = 0; l < sliceSegment.byteSize(); l++) {
|
||||
assertEquals((byte) byteHandle.get(sliceSegment.baseAddress(), l), ~value);
|
||||
}
|
||||
// assert enclosing slice
|
||||
assertEquals((byte) byteHandle.get(segment.baseAddress(), 0L), value);
|
||||
for (long l = 1; l < segment.byteSize() - 2; l++) {
|
||||
assertEquals((byte) byteHandle.get(segment.baseAddress(), l), (byte) ~value);
|
||||
}
|
||||
assertEquals((byte) byteHandle.get(segment.baseAddress(), segment.byteSize() - 1L), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "segmentFactories", expectedExceptions = IllegalStateException.class)
|
||||
public void testFillClosed(Supplier<MemorySegment> memorySegmentSupplier) {
|
||||
MemorySegment segment = memorySegmentSupplier.get();
|
||||
segment.close();
|
||||
segment.fill((byte) 0xFF);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class)
|
||||
public void testFillIllegalAccessMode(Supplier<MemorySegment> memorySegmentSupplier) {
|
||||
try (MemorySegment segment = memorySegmentSupplier.get()) {
|
||||
segment.withAccessModes(segment.accessModes() & ~WRITE).fill((byte) 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "segmentFactories")
|
||||
public void testFillThread(Supplier<MemorySegment> memorySegmentSupplier) throws Exception {
|
||||
try (MemorySegment segment = memorySegmentSupplier.get()) {
|
||||
AtomicReference<RuntimeException> exception = new AtomicReference<>();
|
||||
Runnable action = () -> {
|
||||
try {
|
||||
segment.fill((byte) 0xBA);
|
||||
} catch (RuntimeException e) {
|
||||
exception.set(e);
|
||||
}
|
||||
};
|
||||
Thread thread = new Thread(action);
|
||||
thread.start();
|
||||
thread.join();
|
||||
|
||||
RuntimeException e = exception.get();
|
||||
if (!(e instanceof IllegalStateException)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFillEmpty() {
|
||||
MemorySegment.ofArray(new byte[] { }).fill((byte) 0xFF);
|
||||
MemorySegment.ofArray(new byte[2]).asSlice(0, 0).fill((byte) 0xFF);
|
||||
MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(0)).fill((byte) 0xFF);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testWithAccessModesBadUnsupportedMode() {
|
||||
int[] arr = new int[1];
|
||||
@ -267,6 +336,9 @@ public class TestSegments {
|
||||
|
||||
final static List<String> CONFINED_NAMES = List.of(
|
||||
"close",
|
||||
"fill",
|
||||
"copyFrom",
|
||||
"mismatch",
|
||||
"toByteArray",
|
||||
"withOwnerThread"
|
||||
);
|
||||
|
@ -205,15 +205,13 @@ public class TestSpliterator {
|
||||
};
|
||||
}
|
||||
|
||||
static final int ALL_ACCESS_MODES = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
|
||||
|
||||
@DataProvider(name = "accessScenarios")
|
||||
public Object[][] accessScenarios() {
|
||||
SequenceLayout layout = MemoryLayout.ofSequence(16, MemoryLayouts.JAVA_INT);
|
||||
var mallocSegment = MemorySegment.allocateNative(layout);
|
||||
|
||||
Map<Supplier<Spliterator<MemorySegment>>,Integer> l = Map.of(
|
||||
() -> spliterator(mallocSegment.withAccessModes(ALL_ACCESS_MODES), layout), ALL_ACCESS_MODES,
|
||||
() -> spliterator(mallocSegment.withAccessModes(ALL_ACCESS), layout), ALL_ACCESS,
|
||||
() -> spliterator(mallocSegment.withAccessModes(0), layout), 0,
|
||||
() -> spliterator(mallocSegment.withAccessModes(READ), layout), READ,
|
||||
() -> spliterator(mallocSegment.withAccessModes(CLOSE), layout), 0,
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
package org.openjdk.tests.java.util.stream;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
@ -116,9 +115,9 @@ public class SegmentTestDataProvider {
|
||||
|
||||
static Consumer<MemorySegment> segmentCopier(Consumer<MemorySegment> input) {
|
||||
return segment -> {
|
||||
MemorySegment copy = MemorySegment.ofArray(new byte[(int)segment.byteSize()]);
|
||||
MemoryAddress.copy(segment.baseAddress(), copy.baseAddress(), segment.byteSize());
|
||||
input.accept(copy);
|
||||
MemorySegment dest = MemorySegment.ofArray(new byte[(int)segment.byteSize()]);
|
||||
dest.copyFrom(segment);
|
||||
input.accept(dest);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.openjdk.bench.jdk.incubator.foreign;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@State(org.openjdk.jmh.annotations.Scope.Thread)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@Fork(3)
|
||||
public class BulkOps {
|
||||
|
||||
static final Unsafe unsafe = Utils.unsafe;
|
||||
|
||||
static final int ELEM_SIZE = 1_000_000;
|
||||
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
|
||||
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
|
||||
|
||||
static final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE);
|
||||
static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE);
|
||||
|
||||
static final int[] bytes = new int[ELEM_SIZE];
|
||||
static final MemorySegment bytesSegment = MemorySegment.ofArray(bytes);
|
||||
static final int UNSAFE_INT_OFFSET = unsafe.arrayBaseOffset(int[].class);
|
||||
|
||||
static {
|
||||
for (int i = 0 ; i < bytes.length ; i++) {
|
||||
bytes[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public void unsafe_fill() {
|
||||
unsafe.setMemory(unsafe_addr, ALLOC_SIZE, (byte)42);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public void segment_fill() {
|
||||
segment.fill((byte)42);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public void unsafe_copy() {
|
||||
unsafe.copyMemory(bytes, UNSAFE_INT_OFFSET, null, unsafe_addr, ALLOC_SIZE);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public void segment_copy() {
|
||||
segment.copyFrom(bytesSegment);
|
||||
}
|
||||
}
|
@ -95,7 +95,7 @@ public class LoopOverNonConstantMapped {
|
||||
}
|
||||
((MappedByteBuffer)byteBuffer).force();
|
||||
}
|
||||
segment = MemorySegment.mapFromPath(tempPath, ALLOC_SIZE, FileChannel.MapMode.READ_WRITE);
|
||||
segment = MemorySegment.mapFromPath(tempPath, 0L, ALLOC_SIZE, FileChannel.MapMode.READ_WRITE);
|
||||
unsafe_addr = segment.baseAddress().toRawLongValue();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.openjdk.bench.jdk.incubator.foreign;
|
||||
|
||||
import jdk.incubator.foreign.MemoryAddress;
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemoryLayout;
|
||||
import jdk.incubator.foreign.MemoryLayouts;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@State(org.openjdk.jmh.annotations.Scope.Thread)
|
||||
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||
@Fork(3)
|
||||
public class TestAdaptVarHandles {
|
||||
|
||||
static class IntBox {
|
||||
|
||||
private final int value;
|
||||
|
||||
IntBox(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
int intValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static final int ELEM_SIZE = 1_000_000;
|
||||
|
||||
static final MethodHandle INT_TO_INTBOX;
|
||||
static final MethodHandle INTBOX_TO_INT;
|
||||
|
||||
static {
|
||||
try {
|
||||
INT_TO_INTBOX = MethodHandles.lookup()
|
||||
.findConstructor(IntBox.class, MethodType.methodType(void.class, int.class));
|
||||
INTBOX_TO_INT = MethodHandles.lookup()
|
||||
.findVirtual(IntBox.class, "intValue", MethodType.methodType(int.class));
|
||||
} catch (Throwable ex) {
|
||||
throw new ExceptionInInitializerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static final VarHandle VH_int = MethodHandles.arrayElementVarHandle(int[].class);
|
||||
|
||||
static final VarHandle VH_box_int = MemoryHandles.filterValue(VH_int, INTBOX_TO_INT, INT_TO_INTBOX);
|
||||
|
||||
static final VarHandle VH_addr_int = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)
|
||||
.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
|
||||
|
||||
static final VarHandle VH_addr_box_int = MemoryHandles.filterValue(VH_addr_int, INTBOX_TO_INT, INT_TO_INTBOX);
|
||||
|
||||
static final MethodHandle MH_int = VH_int.toMethodHandle(VarHandle.AccessMode.GET);
|
||||
|
||||
static final MethodHandle MH_box_int = MethodHandles.filterReturnValue(MH_int, INT_TO_INTBOX);
|
||||
|
||||
int[] base = new int[ELEM_SIZE];
|
||||
MemorySegment segment = MemorySegment.ofArray(base);
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
for (int i = 0; i < ELEM_SIZE; i++) {
|
||||
base[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int vh_loop() throws Throwable {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < ELEM_SIZE; i++) {
|
||||
sum += (int)VH_int.get(base, i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int vh_box_loop() throws Throwable {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < ELEM_SIZE; i++) {
|
||||
sum += ((IntBox)VH_box_int.get(base, i)).intValue();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int mh_loop() throws Throwable {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < ELEM_SIZE; i++) {
|
||||
sum += (int)MH_int.invokeExact(base, i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int mh_box_loop() throws Throwable {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < ELEM_SIZE; i++) {
|
||||
sum += ((IntBox)MH_box_int.invokeExact(base, i)).intValue();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int segment_loop() throws Throwable {
|
||||
int sum = 0;
|
||||
MemoryAddress baseAddress = segment.baseAddress();
|
||||
for (int i = 0; i < ELEM_SIZE; i++) {
|
||||
sum += (int)VH_addr_int.get(baseAddress, (long)i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int segment_box_loop() throws Throwable {
|
||||
int sum = 0;
|
||||
MemoryAddress baseAddress = segment.baseAddress();
|
||||
for (int i = 0; i < ELEM_SIZE; i++) {
|
||||
sum += ((IntBox)VH_addr_box_int.get(baseAddress, (long)i)).intValue();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user