diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java index db8684d9636..77f30636d1b 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -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); diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index e92cfef2e9d..e7dd83cd6f5 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -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: + *
+ *{@code + * T target(A...) + * V filter(B... , T) + * V adapter(A... a, B... b) { + * T t = target(a...); + * return filter(b..., t); + * }
+ * 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
diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java
index aaab1f43967..60d90104a98 100644
--- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java
+++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java
@@ -356,43 +356,97 @@ final class VarHandles {
noCheckedExceptions(filterToTarget);
noCheckedExceptions(filterFromTarget);
+ List Mapped memory segments are created via the {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}.
+ * 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()}.
*
diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java
index 104519c0676..dfd85559063 100644
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java
@@ -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)}.
- *
- * 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 unchecked memory address instance modelling the {@code NULL} address. This address is not backed by
* a memory segment and hence it cannot be dereferenced.
diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java
index ecbcfc2cd90..9e0abe8e1aa 100644
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java
@@ -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.
+ *
+ * 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 unsigned short 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:
+ *
+ * When calling e.g. {@link VarHandle#set(Object...)} on the resulting var
+ * handle, the incoming value (of type {@code adaptedType}) is converted by a
+ * narrowing primitive conversion 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 unsigned widening conversion 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.
+ *
+ * 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.
*
* 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 last 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 last 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.
*
- * 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).
*
- * 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);
}
diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java
index 13712fd2c77..4c3efa16668 100644
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java
@@ -97,7 +97,7 @@ import java.util.stream.Stream;
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
*
* 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,
));
* }
*
- * We can obtain the offset of the member layout named
* 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 mapped memory segments
+ * {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}. Such memory segments are called mapped memory segments
* (see {@link MappedMemorySegment}).
*
*
+ * More specifically, the given value is filled into each address of this
+ * segment. Equivalent to (but likely more efficient than) the following code:
+ *
+ *
+ * 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 alive, 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}.
+ *
+ * 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).
+ *
+ * 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 immutable
@@ -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).
*
- * The segment will feature all access modes, unless the given
- * buffer is {@linkplain ByteBuffer#isReadOnly() read-only} in which case the segment will
+ * The segment will feature all access modes (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.
*
* The resulting memory segment keeps a reference to the backing buffer, to ensure it remains reachable
@@ -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.
*
* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable
- * for the life-time of the segment. The segment will feature all access modes.
+ * for the life-time of the segment. The segment will feature all access modes
+ * (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.
*
* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable
- * for the life-time of the segment. The segment will feature all access modes.
+ * for the life-time of the segment. The segment will feature all access modes
+ * (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.
*
* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable
- * for the life-time of the segment. The segment will feature all access modes.
+ * for the life-time of the segment. The segment will feature all access modes
+ * (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.
*
* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable
- * for the life-time of the segment. The segment will feature all access modes.
+ * for the life-time of the segment. The segment will feature all access modes
+ * (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.
*
* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable
- * for the life-time of the segment. The segment will feature all access modes.
+ * for the life-time of the segment. The segment will feature all access modes
+ * (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.
*
* The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable
- * for the life-time of the segment. The segment will feature all access modes.
+ * for the life-time of the segment. The segment will feature all access modes
+ * (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.
*
- * The segment will feature all access modes, 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 access modes (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 access modes.
+ * alignment constraint (in bytes). The segment will feature all access modes
+ * (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 access modes.
+ * (often as a plain {@code long} value). The segment will feature all access modes
+ * (see {@link #ALL_ACCESS}).
*
* This method is restricted. 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;
}
diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
index af1eff3361d..2bfe7926e8d 100644
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
@@ -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;
}
diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java
index 006ad844e50..c5321195b09 100644
--- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java
@@ -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) {
diff --git a/test/jdk/java/foreign/TestAdaptVarHandles.java b/test/jdk/java/foreign/TestAdaptVarHandles.java
index 5eec649cdff..0ab4adf4656 100644
--- a/test/jdk/java/foreign/TestAdaptVarHandles.java
+++ b/test/jdk/java/foreign/TestAdaptVarHandles.java
@@ -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);
+ }
}
diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java
index 07f014f0ade..984763907a6 100644
--- a/test/jdk/java/foreign/TestByteBuffer.java
+++ b/test/jdk/java/foreign/TestByteBuffer.java
@@ -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
+ * {@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
+ * }
value
from seq
, as follows:
+ * We can obtain the offset, in bits, of the member layout named value
from seq
, as follows:
*
*
* 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.
diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java
index 8dddbb87f05..d15ade978f7 100644
--- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java
+++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java
@@ -74,7 +74,7 @@ import java.util.function.Consumer;
* by native memory.
* {@code
-long valueOffset = seq.addOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
+long valueOffset = seq.bitOffset(PathElement.sequenceElement(), PathElement.groupElement("value"));
* }
Closing a memory segment
@@ -286,6 +286,82 @@ public interface MemorySegment extends AutoCloseable {
*/
void close();
+ /**
+ * Fills a value into this memory segment.
+ * {@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);
+}
+ * }
+ *
+ * without any regard or guarantees on the ordering of particular memory
+ * elements being set.
+ *