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:
Chris Hegarty 2020-06-03 16:50:03 +01:00 committed by Maurizio Cimadamore
parent eec7750e55
commit f1e1cb7055
25 changed files with 1634 additions and 185 deletions

@ -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)));

@ -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);
}
}

@ -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;
}
}