7cb2e6d65b
Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Reviewed-by: jvernee
1073 lines
46 KiB
Java
1073 lines
46 KiB
Java
/*
|
|
* Copyright (c) 2019, 2023, 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
|
|
* @modules java.base/sun.nio.ch java.base/jdk.internal.foreign
|
|
* @run testng/othervm/timeout=600 --enable-native-access=ALL-UNNAMED TestByteBuffer
|
|
*/
|
|
|
|
import java.lang.foreign.*;
|
|
import java.lang.foreign.MemoryLayout.PathElement;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.lang.invoke.MethodHandle;
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.lang.invoke.VarHandle;
|
|
import java.lang.ref.Cleaner;
|
|
import java.lang.ref.Reference;
|
|
import java.lang.ref.WeakReference;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.net.URI;
|
|
import java.nio.Buffer;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.nio.CharBuffer;
|
|
import java.nio.DoubleBuffer;
|
|
import java.nio.FloatBuffer;
|
|
import java.nio.IntBuffer;
|
|
import java.nio.LongBuffer;
|
|
import java.nio.MappedByteBuffer;
|
|
import java.nio.ShortBuffer;
|
|
import java.nio.channels.FileChannel;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.StandardOpenOption;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.function.BiConsumer;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
import java.util.stream.Stream;
|
|
|
|
import org.testng.SkipException;
|
|
import org.testng.annotations.*;
|
|
import sun.nio.ch.DirectBuffer;
|
|
|
|
import static java.lang.foreign.ValueLayout.*;
|
|
import static org.testng.Assert.*;
|
|
|
|
public class TestByteBuffer {
|
|
|
|
static final Path tempPath;
|
|
|
|
static {
|
|
try {
|
|
File file = File.createTempFile("buffer", "txt");
|
|
file.deleteOnExit();
|
|
tempPath = file.toPath();
|
|
Files.write(file.toPath(), new byte[256], StandardOpenOption.WRITE);
|
|
|
|
} catch (IOException ex) {
|
|
throw new ExceptionInInitializerError(ex);
|
|
}
|
|
}
|
|
|
|
static final ValueLayout.OfChar BB_CHAR = JAVA_CHAR_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
|
|
static final ValueLayout.OfShort BB_SHORT = JAVA_SHORT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
|
|
static final ValueLayout.OfInt BB_INT = JAVA_INT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
|
|
static final ValueLayout.OfLong BB_LONG = JAVA_LONG_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
|
|
static final ValueLayout.OfFloat BB_FLOAT = JAVA_FLOAT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
|
|
static final ValueLayout.OfDouble BB_DOUBLE = JAVA_DOUBLE_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
|
|
|
|
static SequenceLayout tuples = MemoryLayout.sequenceLayout(500,
|
|
MemoryLayout.structLayout(
|
|
BB_INT.withName("index"),
|
|
BB_FLOAT.withName("value")
|
|
));
|
|
|
|
static final SequenceLayout bytes = MemoryLayout.sequenceLayout(100, JAVA_BYTE);
|
|
static final SequenceLayout chars = MemoryLayout.sequenceLayout(100, BB_CHAR);
|
|
static final SequenceLayout shorts = MemoryLayout.sequenceLayout(100, BB_SHORT);
|
|
static final SequenceLayout ints = MemoryLayout.sequenceLayout(100, BB_INT);
|
|
static final SequenceLayout floats = MemoryLayout.sequenceLayout(100, BB_FLOAT);
|
|
static final SequenceLayout longs = MemoryLayout.sequenceLayout(100, BB_LONG);
|
|
static final SequenceLayout doubles = MemoryLayout.sequenceLayout(100, BB_DOUBLE);
|
|
|
|
static final VarHandle indexHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("index"));
|
|
static final VarHandle valueHandle = tuples.varHandle(PathElement.sequenceElement(), PathElement.groupElement("value"));
|
|
|
|
static void initTuples(MemorySegment base, long count) {
|
|
for (long i = 0; i < count ; i++) {
|
|
indexHandle.set(base, 0L, i, (int)i);
|
|
valueHandle.set(base, 0L, i, (float)(i / 500f));
|
|
}
|
|
}
|
|
|
|
static void checkTuples(MemorySegment base, ByteBuffer bb, long count) {
|
|
for (long i = 0; i < count ; i++) {
|
|
int index;
|
|
float value;
|
|
assertEquals(index = bb.getInt(), (int)indexHandle.get(base, 0L, i));
|
|
assertEquals(value = bb.getFloat(), (float)valueHandle.get(base, 0L, i));
|
|
assertEquals(value, index / 500f);
|
|
}
|
|
}
|
|
|
|
static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {
|
|
for (long i = 0; i < seq.elementCount() ; i++) {
|
|
handleSetter.accept(base, i);
|
|
}
|
|
}
|
|
|
|
static <Z extends Buffer> void checkBytes(MemorySegment base, SequenceLayout layout,
|
|
Function<ByteBuffer, Z> bufFactory,
|
|
BiFunction<MemorySegment, Long, Object> handleExtractor,
|
|
Function<Z, Object> bufferExtractor) {
|
|
long nelems = layout.elementCount();
|
|
long elemSize = layout.elementLayout().byteSize();
|
|
for (long i = 0 ; i < nelems ; i++) {
|
|
long limit = nelems - i;
|
|
MemorySegment resizedSegment = base.asSlice(i * elemSize, limit * elemSize);
|
|
ByteBuffer bb = resizedSegment.asByteBuffer();
|
|
Z z = bufFactory.apply(bb);
|
|
MemorySegment segmentBufferView = MemorySegment.ofBuffer(z);
|
|
for (long j = i ; j < limit ; j++) {
|
|
Object handleValue = handleExtractor.apply(resizedSegment, j - i);
|
|
Object bufferValue = bufferExtractor.apply(z);
|
|
Object handleViewValue = handleExtractor.apply(segmentBufferView, j - i);
|
|
if (handleValue instanceof Number) {
|
|
assertEquals(((Number)handleValue).longValue(), j);
|
|
assertEquals(((Number)bufferValue).longValue(), j);
|
|
assertEquals(((Number)handleViewValue).longValue(), j);
|
|
} else {
|
|
assertEquals((long)(char)handleValue, j);
|
|
assertEquals((long)(char)bufferValue, j);
|
|
assertEquals((long)(char)handleViewValue, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testOffheap() {
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment segment = arena.allocate(tuples);;
|
|
initTuples(segment, tuples.elementCount());
|
|
|
|
ByteBuffer bb = segment.asByteBuffer();
|
|
checkTuples(segment, bb, tuples.elementCount());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testHeap() {
|
|
byte[] arr = new byte[(int) tuples.byteSize()];
|
|
MemorySegment region = MemorySegment.ofArray(arr);
|
|
initTuples(region, tuples.elementCount());
|
|
|
|
ByteBuffer bb = region.asByteBuffer();
|
|
checkTuples(region, bb, tuples.elementCount());
|
|
}
|
|
|
|
@Test
|
|
public void testChannel() throws Throwable {
|
|
File f = new File("test.out");
|
|
assertTrue(f.createNewFile());
|
|
f.deleteOnExit();
|
|
|
|
//write to channel
|
|
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
|
|
MemorySegment segment = MemorySegment.ofBuffer(mbb);
|
|
initTuples(segment, tuples.elementCount());
|
|
mbb.force();
|
|
});
|
|
}
|
|
|
|
//read from channel
|
|
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
|
|
withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {
|
|
MemorySegment segment = MemorySegment.ofBuffer(mbb);
|
|
checkTuples(segment, mbb, tuples.elementCount());
|
|
});
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDefaultAccessModesMappedSegment() throws Throwable {
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena);
|
|
assertFalse(segment.isReadOnly());
|
|
}
|
|
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(tempPath, StandardOpenOption.READ)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 8L, arena);
|
|
assertTrue(segment.isReadOnly());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMappedSegment() throws Throwable {
|
|
File f = new File("test2.out");
|
|
f.createNewFile();
|
|
f.deleteOnExit();
|
|
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
//write to channel
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, tuples.byteSize(), arena);
|
|
initTuples(segment, tuples.elementCount());
|
|
segment.force();
|
|
}
|
|
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
|
|
//read from channel
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena);
|
|
checkTuples(segment, segment.asByteBuffer(), tuples.elementCount());
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider = "mappedOps", expectedExceptions = IllegalStateException.class)
|
|
public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable {
|
|
File f = new File("test3.out");
|
|
f.createNewFile();
|
|
f.deleteOnExit();
|
|
|
|
Arena arena = Arena.ofConfined();
|
|
try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 8L, arena);
|
|
assertTrue(segment.isMapped());
|
|
arena.close();
|
|
mappedBufferOp.apply(segment);
|
|
}
|
|
}
|
|
|
|
@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()) {
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
//write to channel
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, i, tuples.byteSize(), arena);
|
|
initTuples(segment, 1);
|
|
segment.force();
|
|
}
|
|
}
|
|
|
|
// check one at a time
|
|
for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
|
|
//read from channel
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, tuples.byteSize(), arena);
|
|
checkTuples(segment, segment.asByteBuffer(), 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider = "fromArrays")
|
|
public void testAsByteBufferFromNonByteArray(MemorySegment segment) {
|
|
if (!segment.heapBase().map(a -> a instanceof byte[]).get()) {
|
|
// This should not work as the segment is not backed by a byte array
|
|
assertThrows(UnsupportedOperationException.class, segment::asByteBuffer);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMappedSegmentAsByteBuffer() throws Throwable {
|
|
File f = new File("test4.out");
|
|
assertTrue(f.createNewFile());
|
|
f.deleteOnExit();
|
|
|
|
for (var mapOption : List.of(FileChannel.MapMode.READ_WRITE, FileChannel.MapMode.READ_ONLY, FileChannel.MapMode.PRIVATE)) {
|
|
for (var arena : List.of(Arena.ofConfined(), Arena.global())) {
|
|
try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
//write to channel
|
|
MemorySegment segment = fileChannel.map(mapOption, 0L, 32L, arena);
|
|
segment.force();
|
|
segment.load();
|
|
segment.isLoaded();
|
|
segment.unload();
|
|
ByteBuffer byteBuffer = segment.asByteBuffer();
|
|
assertEquals(byteBuffer.capacity(), segment.byteSize());
|
|
assertEquals(byteBuffer.isReadOnly(), segment.isReadOnly());
|
|
assertTrue(byteBuffer.isDirect());
|
|
} catch (IOException e) {
|
|
if (e.getMessage().equals("Function not implemented"))
|
|
throw new SkipException(e.getMessage(), e);
|
|
} finally {
|
|
if (arena.scope() != Arena.global().scope()) {
|
|
arena.close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static final long LARGE_SIZE = (2L * 1024L + 512L) * 1024L * 1024L; // 2.5 GiB
|
|
|
|
@Test
|
|
public void testLargeMappedSegment() throws Throwable {
|
|
if (System.getProperty("sun.arch.data.model").equals("32")) {
|
|
throw new SkipException("large mapped files not supported on 32-bit systems");
|
|
}
|
|
|
|
File f = new File("testLargeMappedSegment.out");
|
|
f.createNewFile();
|
|
f.deleteOnExit();
|
|
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, LARGE_SIZE, arena);
|
|
segment.isLoaded();
|
|
segment.load();
|
|
segment.isLoaded();
|
|
segment.force();
|
|
segment.isLoaded();
|
|
segment.unload();
|
|
segment.isLoaded();
|
|
} catch(IOException e) {
|
|
if (e.getMessage().equals("Function not implemented"))
|
|
throw new SkipException(e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
static void withMappedBuffer(FileChannel channel, FileChannel.MapMode mode, long pos, long size, Consumer<MappedByteBuffer> action) throws Throwable {
|
|
MappedByteBuffer mbb = channel.map(mode, pos, size);
|
|
var ref = new WeakReference<>(mbb);
|
|
action.accept(mbb);
|
|
mbb = null;
|
|
//wait for it to be GCed
|
|
System.gc();
|
|
while (ref.get() != null) {
|
|
Thread.sleep(20);
|
|
}
|
|
}
|
|
|
|
static void checkByteArrayAlignment(MemoryLayout layout) {
|
|
if (layout.byteSize() > 4
|
|
&& System.getProperty("sun.arch.data.model").equals("32")) {
|
|
throw new SkipException("avoid unaligned access on 32-bit system");
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider = "bufferOps")
|
|
public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {
|
|
Buffer bb;
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment segment = arena.allocate(bytes);;
|
|
bb = bufferFactory.apply(segment.asByteBuffer());
|
|
}
|
|
//outside of session!!
|
|
try {
|
|
method.invoke(bb, args);
|
|
fail("Exception expected");
|
|
} catch (InvocationTargetException ex) {
|
|
Throwable cause = ex.getCause();
|
|
if (cause instanceof IllegalStateException) {
|
|
//all get/set buffer operation should fail because of the session check
|
|
assertTrue(ex.getCause().getMessage().contains("Already closed"));
|
|
} else {
|
|
//all other exceptions were unexpected - fail
|
|
fail("Unexpected exception", cause);
|
|
}
|
|
} catch (Throwable ex) {
|
|
//unexpected exception - fail
|
|
fail("Unexpected exception", ex);
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider = "bufferHandleOps")
|
|
public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {
|
|
ByteBuffer bb;
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment segment = arena.allocate(bytes);;
|
|
bb = segment.asByteBuffer();
|
|
for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
|
|
MethodHandle handle = e.getKey().bindTo(bufferHandle)
|
|
.asSpreader(Object[].class, e.getValue().length);
|
|
try {
|
|
handle.invoke(e.getValue());
|
|
} catch (UnsupportedOperationException ex) {
|
|
//skip
|
|
} catch (Throwable ex) {
|
|
//should not fail - segment is alive!
|
|
fail();
|
|
}
|
|
}
|
|
}
|
|
for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {
|
|
try {
|
|
MethodHandle handle = e.getKey().bindTo(bufferHandle)
|
|
.asSpreader(Object[].class, e.getValue().length);
|
|
handle.invoke(e.getValue());
|
|
fail();
|
|
} catch (IllegalStateException ex) {
|
|
assertTrue(ex.getMessage().contains("Already closed"));
|
|
} catch (UnsupportedOperationException ex) {
|
|
//skip
|
|
} catch (Throwable ex) {
|
|
fail();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider = "bufferOps")
|
|
public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment segment = arena.allocate(bytes);;
|
|
Buffer bb = bufferFactory.apply(segment.asByteBuffer());
|
|
assertTrue(bb.isDirect());
|
|
DirectBuffer directBuffer = ((DirectBuffer)bb);
|
|
assertEquals(directBuffer.address(), segment.address());
|
|
assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
|
|
assertTrue(directBuffer.cleaner() == null);
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider="resizeOps")
|
|
public void testResizeOffheap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment segment = arena.allocate(seq);;
|
|
initializer.accept(segment);
|
|
checker.accept(segment);
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider="resizeOps")
|
|
public void testResizeHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
|
|
checkByteArrayAlignment(seq.elementLayout());
|
|
int capacity = (int)seq.byteSize();
|
|
MemorySegment base = MemorySegment.ofArray(new byte[capacity]);
|
|
initializer.accept(base);
|
|
checker.accept(base);
|
|
}
|
|
|
|
@Test(dataProvider="resizeOps")
|
|
public void testResizeBuffer(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
|
|
checkByteArrayAlignment(seq.elementLayout());
|
|
int capacity = (int)seq.byteSize();
|
|
MemorySegment base = MemorySegment.ofBuffer(ByteBuffer.wrap(new byte[capacity]));
|
|
initializer.accept(base);
|
|
checker.accept(base);
|
|
}
|
|
|
|
@Test(dataProvider="resizeOps")
|
|
public void testResizeRoundtripHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
|
|
checkByteArrayAlignment(seq.elementLayout());
|
|
int capacity = (int)seq.byteSize();
|
|
byte[] arr = new byte[capacity];
|
|
MemorySegment segment = MemorySegment.ofArray(arr);
|
|
initializer.accept(segment);
|
|
MemorySegment second = MemorySegment.ofBuffer(segment.asByteBuffer());
|
|
checker.accept(second);
|
|
}
|
|
|
|
@Test(dataProvider="resizeOps")
|
|
public void testResizeRoundtripNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment segment = arena.allocate(seq);;
|
|
initializer.accept(segment);
|
|
MemorySegment second = MemorySegment.ofBuffer(segment.asByteBuffer());
|
|
checker.accept(second);
|
|
}
|
|
}
|
|
|
|
@Test(expectedExceptions = IllegalStateException.class)
|
|
public void testBufferOnClosedSession() {
|
|
MemorySegment leaked;
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
leaked = arena.allocate(bytes);;
|
|
}
|
|
ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok
|
|
byteBuffer.get(); // should throw
|
|
}
|
|
|
|
@Test(expectedExceptions = IllegalStateException.class)
|
|
public void testTooBigForByteBuffer() {
|
|
MemorySegment segment = MemorySegment.NULL.reinterpret(Integer.MAX_VALUE + 10L);
|
|
segment.asByteBuffer();
|
|
}
|
|
|
|
@Test(expectedExceptions = IllegalArgumentException.class)
|
|
public void testBadMapNegativeSize() throws IOException {
|
|
File f = new File("testNeg1.out");
|
|
f.createNewFile();
|
|
f.deleteOnExit();
|
|
try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, -1L, Arena.ofAuto());
|
|
}
|
|
}
|
|
|
|
@Test(expectedExceptions = IllegalArgumentException.class)
|
|
public void testBadMapNegativeOffset() throws IOException {
|
|
File f = new File("testNeg2.out");
|
|
f.createNewFile();
|
|
f.deleteOnExit();
|
|
try (FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
fileChannel.map(FileChannel.MapMode.READ_WRITE, -1L, 1L, Arena.ofAuto());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMapOffset() throws IOException {
|
|
File f = new File("testMapOffset.out");
|
|
f.createNewFile();
|
|
f.deleteOnExit();
|
|
|
|
int SIZE = Byte.MAX_VALUE;
|
|
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, SIZE, arena);
|
|
for (byte offset = 0; offset < SIZE; offset++) {
|
|
segment.set(JAVA_BYTE, offset, offset);
|
|
}
|
|
segment.force();
|
|
}
|
|
|
|
for (int offset = 0 ; offset < SIZE ; offset++) {
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, SIZE - offset, arena);
|
|
assertEquals(segment.get(JAVA_BYTE, 0), offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMapZeroSize() throws IOException {
|
|
File f = new File("testPos1.out");
|
|
f.createNewFile();
|
|
f.deleteOnExit();
|
|
//RW
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, arena);
|
|
assertEquals(segment.byteSize(), 0);
|
|
assertEquals(segment.isMapped(), true);
|
|
assertFalse(segment.isReadOnly());
|
|
segment.force();
|
|
segment.load();
|
|
segment.isLoaded();
|
|
segment.unload();
|
|
ByteBuffer byteBuffer = segment.asByteBuffer();
|
|
assertEquals(byteBuffer.capacity(), 0);
|
|
assertFalse(byteBuffer.isReadOnly());
|
|
}
|
|
//RO
|
|
try (Arena arena = Arena.ofConfined();
|
|
FileChannel fileChannel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
|
|
MemorySegment segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0L, 0L, arena);
|
|
assertEquals(segment.byteSize(), 0);
|
|
assertEquals(segment.isMapped(), true);
|
|
assertTrue(segment.isReadOnly());
|
|
segment.force();
|
|
segment.load();
|
|
segment.isLoaded();
|
|
segment.unload();
|
|
ByteBuffer byteBuffer = segment.asByteBuffer();
|
|
assertEquals(byteBuffer.capacity(), 0);
|
|
assertTrue(byteBuffer.isReadOnly());
|
|
}
|
|
}
|
|
|
|
@Test(expectedExceptions = UnsupportedOperationException.class)
|
|
public void testMapCustomPath() throws IOException {
|
|
Path path = Path.of(URI.create("jrt:/"));
|
|
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto());
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider="resizeOps")
|
|
public void testCopyHeapToNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
|
|
checkByteArrayAlignment(seq.elementLayout());
|
|
int bytes = (int)seq.byteSize();
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment nativeArray = arena.allocate(bytes, 1);;
|
|
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);
|
|
initializer.accept(heapArray);
|
|
nativeArray.copyFrom(heapArray);
|
|
checker.accept(nativeArray);
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider="resizeOps")
|
|
public void testCopyNativeToHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
|
|
checkByteArrayAlignment(seq.elementLayout());
|
|
int bytes = (int)seq.byteSize();
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment nativeArray = arena.allocate(seq);;
|
|
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);
|
|
initializer.accept(nativeArray);
|
|
heapArray.copyFrom(nativeArray);
|
|
checker.accept(heapArray);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDefaultAccessModesOfBuffer() {
|
|
ByteBuffer rwBuffer = ByteBuffer.wrap(new byte[4]);
|
|
{
|
|
MemorySegment segment = MemorySegment.ofBuffer(rwBuffer);
|
|
assertFalse(segment.isReadOnly());
|
|
}
|
|
|
|
{
|
|
ByteBuffer roBuffer = rwBuffer.asReadOnlyBuffer();
|
|
MemorySegment segment = MemorySegment.ofBuffer(roBuffer);
|
|
assertTrue(segment.isReadOnly());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testOfBufferScopeReachable() throws InterruptedException {
|
|
ByteBuffer buffer = ByteBuffer.allocateDirect(1000);
|
|
MemorySegment segment = MemorySegment.ofBuffer(buffer);
|
|
try {
|
|
AtomicBoolean reachable = new AtomicBoolean(true);
|
|
Cleaner.create().register(buffer, () -> {
|
|
reachable.set(false);
|
|
});
|
|
buffer = null;
|
|
System.gc();
|
|
// let's sleep to let cleaner run
|
|
Thread.sleep(100);
|
|
segment.get(JAVA_BYTE, 0);
|
|
if (!reachable.get()) {
|
|
throw new IllegalStateException();
|
|
}
|
|
} finally {
|
|
Reference.reachabilityFence(segment);
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider="bufferSources")
|
|
public void testBufferToSegment(ByteBuffer bb, Predicate<MemorySegment> segmentChecker) {
|
|
MemorySegment segment = MemorySegment.ofBuffer(bb);
|
|
assertEquals(segment.isReadOnly(), bb.isReadOnly());
|
|
assertTrue(segmentChecker.test(segment));
|
|
assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
|
|
assertEquals(bb.capacity(), segment.byteSize());
|
|
//another round trip
|
|
segment = MemorySegment.ofBuffer(segment.asByteBuffer());
|
|
assertEquals(segment.isReadOnly(), bb.isReadOnly());
|
|
assertTrue(segmentChecker.test(segment));
|
|
assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));
|
|
assertEquals(bb.capacity(), segment.byteSize());
|
|
}
|
|
|
|
@Test(dataProvider="bufferSources")
|
|
public void bufferProperties(ByteBuffer bb, Predicate<MemorySegment> _unused) {
|
|
MemorySegment segment = MemorySegment.ofBuffer(bb);
|
|
ByteBuffer buffer = segment.asByteBuffer();
|
|
assertEquals(buffer.position(), 0);
|
|
assertEquals(buffer.capacity(), segment.byteSize());
|
|
assertEquals(buffer.limit(), segment.byteSize());
|
|
}
|
|
|
|
@Test
|
|
public void testRoundTripAccess() {
|
|
try (Arena arena = Arena.ofConfined()) {
|
|
MemorySegment ms = arena.allocate(4, 1);;
|
|
MemorySegment msNoAccess = ms.asReadOnly();
|
|
MemorySegment msRoundTrip = MemorySegment.ofBuffer(msNoAccess.asByteBuffer());
|
|
assertEquals(msRoundTrip.scope(), ms.scope());
|
|
assertEquals(msNoAccess.isReadOnly(), msRoundTrip.isReadOnly());
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider = "bufferFactories")
|
|
public void testDerivedBufferScopes(Supplier<Buffer> bufferFactory) {
|
|
MemorySegment segment = MemorySegment.ofBuffer(bufferFactory.get());
|
|
assertEquals(segment.scope(), segment.scope());
|
|
// one level
|
|
assertEquals(segment.asSlice(0).scope(), segment.scope());
|
|
assertEquals(segment.asReadOnly().scope(), segment.scope());
|
|
// two levels
|
|
assertEquals(segment.asSlice(0).asReadOnly().scope(), segment.scope());
|
|
assertEquals(segment.asReadOnly().asSlice(0).scope(), segment.scope());
|
|
// check fresh every time
|
|
MemorySegment another = MemorySegment.ofBuffer(bufferFactory.get());
|
|
assertNotEquals(segment.scope(), another.scope());
|
|
}
|
|
|
|
@Test(expectedExceptions = IllegalStateException.class)
|
|
public void testDeadAccessOnClosedBufferSegment() {
|
|
Arena arena = Arena.ofConfined();
|
|
MemorySegment s1 = arena.allocate(JAVA_INT);
|
|
MemorySegment s2 = MemorySegment.ofBuffer(s1.asByteBuffer());
|
|
|
|
// memory freed
|
|
arena.close();
|
|
|
|
s2.set(JAVA_INT, 0, 10); // Dead access!
|
|
}
|
|
|
|
@Test(dataProvider = "closeableArenas")
|
|
public void closeableArenas(Supplier<Arena> arenaSupplier) throws IOException {
|
|
File tmp = File.createTempFile("tmp", "txt");
|
|
tmp.deleteOnExit();
|
|
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE) ;
|
|
Arena arena = arenaSupplier.get()) {
|
|
MemorySegment segment = arena.allocate(10, 1);;
|
|
for (int i = 0; i < 10; i++) {
|
|
segment.set(JAVA_BYTE, i, (byte) i);
|
|
}
|
|
ByteBuffer bb = segment.asByteBuffer();
|
|
assertEquals(channel.write(bb), 10);
|
|
segment.fill((byte)0x00);
|
|
assertEquals(bb.clear(), ByteBuffer.wrap(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
|
|
assertEquals(channel.position(0).read(bb.clear()), 10);
|
|
assertEquals(bb.flip(), ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
|
|
}
|
|
}
|
|
|
|
static final Class<IllegalStateException> ISE = IllegalStateException.class;
|
|
|
|
@Test(dataProvider = "closeableArenas")
|
|
public void testIOOnClosedSegmentBuffer(Supplier<Arena> arenaSupplier) throws IOException {
|
|
File tmp = File.createTempFile("tmp", "txt");
|
|
tmp.deleteOnExit();
|
|
Arena arena = arenaSupplier.get();
|
|
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
MemorySegment segment = arena.allocate(10, 1);
|
|
for (int i = 0; i < 10; i++) {
|
|
segment.set(JAVA_BYTE, i, (byte) i);
|
|
}
|
|
ByteBuffer bb = segment.asByteBuffer();
|
|
arena.close();
|
|
assertThrows(ISE, () -> channel.read(bb));
|
|
assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}));
|
|
assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}, 0, 1));
|
|
assertThrows(ISE, () -> channel.write(bb));
|
|
assertThrows(ISE, () -> channel.write(new ByteBuffer[] {bb}));
|
|
assertThrows(ISE, () -> channel.write(new ByteBuffer[] {bb}, 0 ,1));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void buffersAndArraysFromSlices() {
|
|
try (Arena arena = Arena.ofShared()) {
|
|
MemorySegment segment = arena.allocate(16, 1);;
|
|
int newSize = 8;
|
|
var slice = segment.asSlice(4, newSize);
|
|
|
|
var bytes = slice.toArray(JAVA_BYTE);
|
|
assertEquals(newSize, bytes.length);
|
|
|
|
var buffer = slice.asByteBuffer();
|
|
// Fails for heap segments, but passes for native segments:
|
|
assertEquals(0, buffer.position());
|
|
assertEquals(newSize, buffer.limit());
|
|
assertEquals(newSize, buffer.capacity());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void viewsFromSharedSegment() {
|
|
try (Arena arena = Arena.ofShared()) {
|
|
MemorySegment segment = arena.allocate(16, 1);;
|
|
var byteBuffer = segment.asByteBuffer();
|
|
byteBuffer.asReadOnlyBuffer();
|
|
byteBuffer.slice(0, 8);
|
|
}
|
|
}
|
|
|
|
@DataProvider(name = "segments")
|
|
public static Object[][] segments() throws Throwable {
|
|
return new Object[][] {
|
|
{ (Supplier<MemorySegment>) () -> Arena.ofAuto().allocate(16, 1)},
|
|
{ (Supplier<MemorySegment>) () -> Arena.ofConfined().allocate(16, 1)},
|
|
{ (Supplier<MemorySegment>) () -> MemorySegment.ofArray(new byte[16]) }
|
|
};
|
|
}
|
|
|
|
@DataProvider(name = "closeableArenas")
|
|
public static Object[][] closeableArenas() {
|
|
return new Object[][] {
|
|
{ (Supplier<Arena>) Arena::ofConfined},
|
|
{ (Supplier<Arena>) Arena::ofShared},
|
|
};
|
|
}
|
|
|
|
@DataProvider(name = "bufferOps")
|
|
public static Object[][] bufferOps() throws Throwable {
|
|
List<Object[]> args = new ArrayList<>();
|
|
bufferOpsArgs(args, bb -> bb, ByteBuffer.class);
|
|
bufferOpsArgs(args, ByteBuffer::asCharBuffer, CharBuffer.class);
|
|
bufferOpsArgs(args, ByteBuffer::asShortBuffer, ShortBuffer.class);
|
|
bufferOpsArgs(args, ByteBuffer::asIntBuffer, IntBuffer.class);
|
|
bufferOpsArgs(args, ByteBuffer::asFloatBuffer, FloatBuffer.class);
|
|
bufferOpsArgs(args, ByteBuffer::asLongBuffer, LongBuffer.class);
|
|
bufferOpsArgs(args, ByteBuffer::asDoubleBuffer, DoubleBuffer.class);
|
|
return args.toArray(Object[][]::new);
|
|
}
|
|
|
|
static void bufferOpsArgs(List<Object[]> argsList, Function<ByteBuffer, Buffer> factory, Class<?> bufferClass) {
|
|
for (Method m : bufferClass.getMethods()) {
|
|
//skip statics and method declared in j.l.Object
|
|
if (m.getDeclaringClass().equals(Object.class)
|
|
|| ((m.getModifiers() & Modifier.STATIC) != 0)
|
|
|| (!m.getName().contains("get") && !m.getName().contains("put"))
|
|
|| m.getParameterCount() > 2) continue;
|
|
Object[] args = Stream.of(m.getParameterTypes())
|
|
.map(TestByteBuffer::defaultValue)
|
|
.toArray();
|
|
argsList.add(new Object[] { factory, m, args });
|
|
}
|
|
}
|
|
|
|
@DataProvider(name = "bufferHandleOps")
|
|
public static Object[][] bufferHandleOps() throws Throwable {
|
|
return new Object[][]{
|
|
{ MethodHandles.byteBufferViewVarHandle(char[].class, ByteOrder.nativeOrder()) },
|
|
{ MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()) },
|
|
{ MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder()) },
|
|
{ MethodHandles.byteBufferViewVarHandle(long[].class, ByteOrder.nativeOrder()) },
|
|
{ MethodHandles.byteBufferViewVarHandle(float[].class, ByteOrder.nativeOrder()) },
|
|
{ MethodHandles.byteBufferViewVarHandle(double[].class, ByteOrder.nativeOrder()) }
|
|
};
|
|
}
|
|
|
|
static Map<MethodHandle, Object[]> varHandleMembers(ByteBuffer bb, VarHandle handle) {
|
|
Map<MethodHandle, Object[]> members = new HashMap<>();
|
|
for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
|
|
Class<?>[] params = handle.accessModeType(mode).parameterArray();
|
|
Object[] args = Stream.concat(Stream.of(bb), Stream.of(params).skip(1)
|
|
.map(TestByteBuffer::defaultValue))
|
|
.toArray();
|
|
try {
|
|
members.put(MethodHandles.varHandleInvoker(mode, handle.accessModeType(mode)), args);
|
|
} catch (Throwable ex) {
|
|
throw new AssertionError(ex);
|
|
}
|
|
}
|
|
return members;
|
|
}
|
|
|
|
@DataProvider(name = "resizeOps")
|
|
public Object[][] resizeOps() {
|
|
Consumer<MemorySegment> byteInitializer =
|
|
(base) -> initBytes(base, bytes, (addr, pos) -> addr.set(JAVA_BYTE, pos, (byte)(long)pos));
|
|
Consumer<MemorySegment> charInitializer =
|
|
(base) -> initBytes(base, chars, (addr, pos) -> addr.setAtIndex(BB_CHAR, pos, (char)(long)pos));
|
|
Consumer<MemorySegment> shortInitializer =
|
|
(base) -> initBytes(base, shorts, (addr, pos) -> addr.setAtIndex(BB_SHORT, pos, (short)(long)pos));
|
|
Consumer<MemorySegment> intInitializer =
|
|
(base) -> initBytes(base, ints, (addr, pos) -> addr.setAtIndex(BB_INT, pos, (int)(long)pos));
|
|
Consumer<MemorySegment> floatInitializer =
|
|
(base) -> initBytes(base, floats, (addr, pos) -> addr.setAtIndex(BB_FLOAT, pos, (float)(long)pos));
|
|
Consumer<MemorySegment> longInitializer =
|
|
(base) -> initBytes(base, longs, (addr, pos) -> addr.setAtIndex(BB_LONG, pos, (long)pos));
|
|
Consumer<MemorySegment> doubleInitializer =
|
|
(base) -> initBytes(base, doubles, (addr, pos) -> addr.setAtIndex(BB_DOUBLE, pos, (double)(long)pos));
|
|
|
|
Consumer<MemorySegment> byteChecker =
|
|
(base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> addr.get(JAVA_BYTE, pos), ByteBuffer::get);
|
|
Consumer<MemorySegment> charChecker =
|
|
(base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> addr.getAtIndex(BB_CHAR, pos), CharBuffer::get);
|
|
Consumer<MemorySegment> shortChecker =
|
|
(base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> addr.getAtIndex(BB_SHORT, pos), ShortBuffer::get);
|
|
Consumer<MemorySegment> intChecker =
|
|
(base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> addr.getAtIndex(BB_INT, pos), IntBuffer::get);
|
|
Consumer<MemorySegment> floatChecker =
|
|
(base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> addr.getAtIndex(BB_FLOAT, pos), FloatBuffer::get);
|
|
Consumer<MemorySegment> longChecker =
|
|
(base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> addr.getAtIndex(BB_LONG, pos), LongBuffer::get);
|
|
Consumer<MemorySegment> doubleChecker =
|
|
(base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> addr.getAtIndex(BB_DOUBLE, pos), DoubleBuffer::get);
|
|
|
|
return new Object[][]{
|
|
{byteChecker, byteInitializer, bytes},
|
|
{charChecker, charInitializer, chars},
|
|
{shortChecker, shortInitializer, shorts},
|
|
{intChecker, intInitializer, ints},
|
|
{floatChecker, floatInitializer, floats},
|
|
{longChecker, longInitializer, longs},
|
|
{doubleChecker, doubleInitializer, doubles}
|
|
};
|
|
}
|
|
|
|
static Object defaultValue(Class<?> c) {
|
|
if (c.isPrimitive()) {
|
|
if (c == char.class) {
|
|
return (char)0;
|
|
} else if (c == boolean.class) {
|
|
return false;
|
|
} else if (c == byte.class) {
|
|
return (byte)0;
|
|
} else if (c == short.class) {
|
|
return (short)0;
|
|
} else if (c == int.class) {
|
|
return 0;
|
|
} else if (c == long.class) {
|
|
return 0L;
|
|
} else if (c == float.class) {
|
|
return 0f;
|
|
} else if (c == double.class) {
|
|
return 0d;
|
|
} else {
|
|
throw new IllegalStateException();
|
|
}
|
|
} else if (c.isArray()) {
|
|
if (c == char[].class) {
|
|
return new char[1];
|
|
} else if (c == boolean[].class) {
|
|
return new boolean[1];
|
|
} else if (c == byte[].class) {
|
|
return new byte[1];
|
|
} else if (c == short[].class) {
|
|
return new short[1];
|
|
} else if (c == int[].class) {
|
|
return new int[1];
|
|
} else if (c == long[].class) {
|
|
return new long[1];
|
|
} else if (c == float[].class) {
|
|
return new float[1];
|
|
} else if (c == double[].class) {
|
|
return new double[1];
|
|
} else {
|
|
throw new IllegalStateException();
|
|
}
|
|
} else if (c == String.class) {
|
|
return "asdf";
|
|
} else if (c == ByteBuffer.class) {
|
|
return ByteBuffer.wrap(new byte[1]);
|
|
} else if (c == CharBuffer.class) {
|
|
return CharBuffer.wrap(new char[1]);
|
|
} else if (c == ShortBuffer.class) {
|
|
return ShortBuffer.wrap(new short[1]);
|
|
} else if (c == IntBuffer.class) {
|
|
return IntBuffer.wrap(new int[1]);
|
|
} else if (c == FloatBuffer.class) {
|
|
return FloatBuffer.wrap(new float[1]);
|
|
} else if (c == LongBuffer.class) {
|
|
return LongBuffer.wrap(new long[1]);
|
|
} else if (c == DoubleBuffer.class) {
|
|
return DoubleBuffer.wrap(new double[1]);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@DataProvider(name = "bufferSources")
|
|
public static Object[][] bufferSources() {
|
|
Predicate<MemorySegment> heapTest = segment -> !segment.isNative() && !segment.isMapped();
|
|
Predicate<MemorySegment> nativeTest = segment -> segment.isNative() && !segment.isMapped();
|
|
Predicate<MemorySegment> mappedTest = segment -> segment.isNative() && segment.isMapped();
|
|
try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
|
|
return new Object[][]{
|
|
{ ByteBuffer.wrap(new byte[256]), heapTest },
|
|
{ ByteBuffer.allocate(256), heapTest },
|
|
{ ByteBuffer.allocateDirect(256), nativeTest },
|
|
{ channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256), mappedTest },
|
|
|
|
{ ByteBuffer.wrap(new byte[256]).asReadOnlyBuffer(), heapTest },
|
|
{ ByteBuffer.allocate(256).asReadOnlyBuffer(), heapTest },
|
|
{ ByteBuffer.allocateDirect(256).asReadOnlyBuffer(), nativeTest },
|
|
{ channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256).asReadOnlyBuffer(),
|
|
mappedTest }
|
|
};
|
|
} catch (IOException ex) {
|
|
throw new ExceptionInInitializerError(ex);
|
|
}
|
|
}
|
|
|
|
enum MappedSegmentOp {
|
|
LOAD(MemorySegment::load),
|
|
UNLOAD(MemorySegment::unload),
|
|
IS_LOADED(MemorySegment::isLoaded),
|
|
FORCE(MemorySegment::force),
|
|
BUFFER_LOAD(m -> ((MappedByteBuffer)m.asByteBuffer()).load()),
|
|
BUFFER_IS_LOADED(m -> ((MappedByteBuffer)m.asByteBuffer()).isLoaded()),
|
|
BUFFER_FORCE(m -> ((MappedByteBuffer)m.asByteBuffer()).force());
|
|
|
|
|
|
private final Consumer<MemorySegment> segmentOp;
|
|
|
|
MappedSegmentOp(Consumer<MemorySegment> segmentOp) {
|
|
this.segmentOp = segmentOp;
|
|
}
|
|
|
|
void apply(MemorySegment segment) {
|
|
segmentOp.accept(segment);
|
|
}
|
|
}
|
|
|
|
@DataProvider(name = "mappedOps")
|
|
public static Object[][] mappedOps() {
|
|
return Stream.of(MappedSegmentOp.values())
|
|
.map(op -> new Object[] { op })
|
|
.toArray(Object[][]::new);
|
|
}
|
|
|
|
@DataProvider(name = "bufferFactories")
|
|
public static Object[][] bufferFactories() {
|
|
List<Supplier<Buffer>> l = List.of(
|
|
() -> ByteBuffer.allocate(10),
|
|
() -> CharBuffer.allocate(10),
|
|
() -> ShortBuffer.allocate(10),
|
|
() -> IntBuffer.allocate(10),
|
|
() -> FloatBuffer.allocate(10),
|
|
() -> LongBuffer.allocate(10),
|
|
() -> DoubleBuffer.allocate(10),
|
|
() -> ByteBuffer.allocateDirect(10),
|
|
() -> ByteBuffer.allocateDirect(10).asCharBuffer(),
|
|
() -> ByteBuffer.allocateDirect(10).asShortBuffer(),
|
|
() -> ByteBuffer.allocateDirect(10).asIntBuffer(),
|
|
() -> ByteBuffer.allocateDirect(10).asFloatBuffer(),
|
|
() -> ByteBuffer.allocateDirect(10).asLongBuffer(),
|
|
() -> ByteBuffer.allocateDirect(10).asDoubleBuffer()
|
|
);
|
|
return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new);
|
|
|
|
}
|
|
@DataProvider(name = "fromArrays")
|
|
public static Object[][] fromArrays() {
|
|
int len = 16;
|
|
return Stream.of(
|
|
MemorySegment.ofArray(new byte[len]),
|
|
MemorySegment.ofArray(new short[len]),
|
|
MemorySegment.ofArray(new char[len]),
|
|
MemorySegment.ofArray(new int[len]),
|
|
MemorySegment.ofArray(new long[len]),
|
|
MemorySegment.ofArray(new float[len]),
|
|
MemorySegment.ofArray(new double[len])
|
|
)
|
|
.map(s -> new Object[] { s })
|
|
.toArray(Object[][]::new);
|
|
}
|
|
}
|