diff a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java @@ -193,10 +193,36 @@ */ static Spliterator spliterator(S segment, SequenceLayout layout) { return AbstractMemorySegmentImpl.spliterator(segment, layout); } + /** + * Fills a value into the given memory segment. + *

+ * More specifically, the given value is filled into each address of the + * segment. Equivalent to (but likely more efficient than) the following 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);
+     * }
+ *

+ * Fill can be useful to initialize or reset the memory of a segment. + * + * @param segment the segment to fill + * @param value the value to fill into the segment + * @throws IllegalStateException if the 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 + * @throws NullPointerException if {@code segment == null} + */ + static void fill(MemorySegment segment, byte value) { + AbstractMemorySegmentImpl.fill(segment, value); + } + /** * The thread owning this segment. * @return the thread owning this segment. */ Thread ownerThread(); diff a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -30,10 +30,11 @@ import jdk.incubator.foreign.SequenceLayout; 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.vm.annotation.ForceInline; import sun.security.action.GetPropertyAction; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; @@ -53,10 +54,12 @@ * are defined for each memory segment kind, see {@link NativeMemorySegmentImpl}, {@link HeapMemorySegmentImpl} and * {@link MappedMemorySegmentImpl}. */ 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 @@ -111,10 +114,19 @@ } return (Spliterator)new SegmentSplitter(sequenceLayout.elementLayout().byteSize(), sequenceLayout.elementCount().getAsLong(), (AbstractMemorySegmentImpl)segment.withAccessModes(segment.accessModes() & ~CLOSE)); } + public static void fill(MemorySegment segment, byte value) { + AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl) segment; + segmentImpl.checkValidState(); + if (!segmentImpl.isSet(WRITE)) { + throw segmentImpl.unsupportedAccessMode(WRITE); + } + UNSAFE.setMemory(segmentImpl.base(), segmentImpl.min(), segmentImpl.length, value); + } + @Override @ForceInline public final MemoryAddress baseAddress() { return new MemoryAddressImpl(this, 0); } diff a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -30,23 +30,24 @@ import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import org.testng.annotations.DataProvider; 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; import java.util.Spliterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.LongFunction; +import java.util.function.Supplier; import java.util.stream.Stream; - +import static jdk.incubator.foreign.MemorySegment.WRITE; import static org.testng.Assert.*; public class TestSegments { @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class) @@ -159,10 +160,106 @@ assertTrue(shouldFail); } } } + @DataProvider(name = "segmentFactories") + public Object[][] segmentFactories() { + List> 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 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()) { + MemorySegment.fill(segment, value); + for (long l = 0; l < segment.byteSize(); l++) { + assertEquals((byte) byteHandle.get(segment.baseAddress(), l), value); + } + + // fill a slice + MemorySegment sliceSegment = segment.asSlice(1, segment.byteSize() - 2); + MemorySegment.fill(sliceSegment, (byte) ~value); + 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 memorySegmentSupplier) { + MemorySegment segment = memorySegmentSupplier.get(); + segment.close(); + MemorySegment.fill(segment, (byte) 0xFF); + } + + @Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class) + public void testFillIllegalAccessMode(Supplier memorySegmentSupplier) { + try (MemorySegment segment = memorySegmentSupplier.get()) { + var readOnlySegment = segment.withAccessModes(segment.accessModes() & ~WRITE); + MemorySegment.fill(readOnlySegment, (byte) 0xFF); + } + } + + @Test(dataProvider = "segmentFactories") + public void testFillThread(Supplier memorySegmentSupplier) throws Exception { + try (MemorySegment segment = memorySegmentSupplier.get()) { + AtomicReference exception = new AtomicReference<>(); + Runnable action = () -> { + try { + MemorySegment.fill(segment, (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() { + try (MemorySegment segment = MemorySegment.ofArray(new byte[] { })) { + MemorySegment.fill(segment, (byte) 0xFF); + } + try (MemorySegment segment = MemorySegment.ofArray(new byte[2]).asSlice(0, 0)) { + MemorySegment.fill(segment, (byte) 0xFF); + } + try (MemorySegment segment = MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(0))) { + MemorySegment.fill(segment, (byte) 0xFF); + } + } + + @Test(expectedExceptions = NullPointerException.class) + public void testFillWithNull() { + MemorySegment.fill(null, (byte) 0xFF); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testBadWithAccessModes() { int[] arr = new int[1]; MemorySegment segment = MemorySegment.ofArray(arr); segment.withAccessModes((1 << AccessActions.values().length) + 1);