Path: blob/master/test/jdk/java/foreign/TestByteBuffer.java
41144 views
/*1* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @modules java.base/sun.nio.ch26* jdk.incubator.foreign/jdk.internal.foreign27* @run testng/othervm --enable-native-access=ALL-UNNAMED TestByteBuffer28*/2930import jdk.incubator.foreign.MemoryAccess;31import jdk.incubator.foreign.MemoryLayouts;32import jdk.incubator.foreign.MemoryLayout;33import jdk.incubator.foreign.MemoryAddress;34import jdk.incubator.foreign.MemorySegment;35import jdk.incubator.foreign.MemoryLayout.PathElement;36import jdk.incubator.foreign.ResourceScope;37import jdk.incubator.foreign.SequenceLayout;3839import java.io.File;40import java.io.IOException;41import java.lang.invoke.MethodHandle;42import java.lang.invoke.MethodHandles;43import java.lang.invoke.VarHandle;44import java.lang.ref.Cleaner;45import java.lang.ref.WeakReference;46import java.lang.reflect.InvocationTargetException;47import java.lang.reflect.Method;48import java.lang.reflect.Modifier;49import java.net.URI;50import java.nio.Buffer;51import java.nio.ByteBuffer;52import java.nio.ByteOrder;53import java.nio.CharBuffer;54import java.nio.DoubleBuffer;55import java.nio.FloatBuffer;56import java.nio.IntBuffer;57import java.nio.LongBuffer;58import java.nio.MappedByteBuffer;59import java.nio.ShortBuffer;60import java.nio.channels.FileChannel;61import java.nio.file.Files;62import java.nio.file.Path;63import java.nio.file.StandardOpenOption;64import java.util.ArrayList;65import java.util.Arrays;66import java.util.HashMap;67import java.util.List;68import java.util.Map;69import java.util.function.BiConsumer;70import java.util.function.BiFunction;71import java.util.function.Consumer;72import java.util.function.Function;73import java.util.function.Predicate;74import java.util.function.Supplier;75import java.util.stream.Stream;7677import jdk.internal.foreign.HeapMemorySegmentImpl;78import jdk.internal.foreign.MappedMemorySegmentImpl;79import jdk.internal.foreign.NativeMemorySegmentImpl;80import org.testng.SkipException;81import org.testng.annotations.*;82import sun.nio.ch.DirectBuffer;8384import static org.testng.Assert.*;8586public class TestByteBuffer {8788static Path tempPath;8990static {91try {92File file = File.createTempFile("buffer", "txt");93file.deleteOnExit();94tempPath = file.toPath();95Files.write(file.toPath(), new byte[256], StandardOpenOption.WRITE);9697} catch (IOException ex) {98throw new ExceptionInInitializerError(ex);99}100}101102static SequenceLayout tuples = MemoryLayout.sequenceLayout(500,103MemoryLayout.structLayout(104MemoryLayouts.BITS_32_BE.withName("index"),105MemoryLayouts.BITS_32_BE.withName("value")106));107108static SequenceLayout bytes = MemoryLayout.sequenceLayout(100,109MemoryLayouts.BITS_8_BE110);111112static SequenceLayout chars = MemoryLayout.sequenceLayout(100,113MemoryLayouts.BITS_16_BE114);115116static SequenceLayout shorts = MemoryLayout.sequenceLayout(100,117MemoryLayouts.BITS_16_BE118);119120static SequenceLayout ints = MemoryLayout.sequenceLayout(100,121MemoryLayouts.BITS_32_BE122);123124static SequenceLayout floats = MemoryLayout.sequenceLayout(100,125MemoryLayouts.BITS_32_BE126);127128static SequenceLayout longs = MemoryLayout.sequenceLayout(100,129MemoryLayouts.BITS_64_BE130);131132static SequenceLayout doubles = MemoryLayout.sequenceLayout(100,133MemoryLayouts.BITS_64_BE134);135136static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index"));137static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value"));138139static void initTuples(MemorySegment base, long count) {140for (long i = 0; i < count ; i++) {141indexHandle.set(base, i, (int)i);142valueHandle.set(base, i, (float)(i / 500f));143}144}145146static void checkTuples(MemorySegment base, ByteBuffer bb, long count) {147for (long i = 0; i < count ; i++) {148int index;149float value;150assertEquals(index = bb.getInt(), (int)indexHandle.get(base, i));151assertEquals(value = bb.getFloat(), (float)valueHandle.get(base, i));152assertEquals(value, index / 500f);153}154}155156static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {157for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {158handleSetter.accept(base, i);159}160}161162static <Z extends Buffer> void checkBytes(MemorySegment base, SequenceLayout layout,163Function<ByteBuffer, Z> bufFactory,164BiFunction<MemorySegment, Long, Object> handleExtractor,165Function<Z, Object> bufferExtractor) {166long nelems = layout.elementCount().getAsLong();167long elemSize = layout.elementLayout().byteSize();168for (long i = 0 ; i < nelems ; i++) {169long limit = nelems - i;170MemorySegment resizedSegment = base.asSlice(i * elemSize, limit * elemSize);171ByteBuffer bb = resizedSegment.asByteBuffer();172Z z = bufFactory.apply(bb);173for (long j = i ; j < limit ; j++) {174Object handleValue = handleExtractor.apply(resizedSegment, j - i);175Object bufferValue = bufferExtractor.apply(z);176if (handleValue instanceof Number) {177assertEquals(((Number)handleValue).longValue(), j);178assertEquals(((Number)bufferValue).longValue(), j);179} else {180assertEquals((long)(char)handleValue, j);181assertEquals((long)(char)bufferValue, j);182}183}184}185}186187@Test188public void testOffheap() {189try (ResourceScope scope = ResourceScope.newConfinedScope()) {190MemorySegment segment = MemorySegment.allocateNative(tuples, scope);191initTuples(segment, tuples.elementCount().getAsLong());192193ByteBuffer bb = segment.asByteBuffer();194checkTuples(segment, bb, tuples.elementCount().getAsLong());195}196}197198@Test199public void testHeap() {200byte[] arr = new byte[(int) tuples.byteSize()];201MemorySegment region = MemorySegment.ofArray(arr);202initTuples(region, tuples.elementCount().getAsLong());203204ByteBuffer bb = region.asByteBuffer();205checkTuples(region, bb, tuples.elementCount().getAsLong());206}207208@Test209public void testChannel() throws Throwable {210File f = new File("test.out");211assertTrue(f.createNewFile());212f.deleteOnExit();213214//write to channel215try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {216withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {217MemorySegment segment = MemorySegment.ofByteBuffer(mbb);218initTuples(segment, tuples.elementCount().getAsLong());219mbb.force();220});221}222223//read from channel224try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {225withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {226MemorySegment segment = MemorySegment.ofByteBuffer(mbb);227checkTuples(segment, mbb, tuples.elementCount().getAsLong());228});229}230}231232@Test233public void testDefaultAccessModesMappedSegment() throws Throwable {234try (ResourceScope scope = ResourceScope.newConfinedScope()) {235MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_WRITE, scope);236assertFalse(segment.isReadOnly());237}238239try (ResourceScope scope = ResourceScope.newConfinedScope()) {240MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_ONLY, scope);241assertTrue(segment.isReadOnly());242}243}244245@Test246public void testMappedSegment() throws Throwable {247File f = new File("test2.out");248f.createNewFile();249f.deleteOnExit();250251try (ResourceScope scope = ResourceScope.newConfinedScope()) {252//write to channel253MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_WRITE, scope);254initTuples(segment, tuples.elementCount().getAsLong());255segment.force();256}257258try (ResourceScope scope = ResourceScope.newConfinedScope()) {259//read from channel260MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY, scope);261checkTuples(segment, segment.asByteBuffer(), tuples.elementCount().getAsLong());262}263}264265@Test(dataProvider = "mappedOps", expectedExceptions = UnsupportedOperationException.class)266public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable {267File f = new File("test3.out");268f.createNewFile();269f.deleteOnExit();270271MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 8, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());272assertTrue(segment.isMapped());273segment.scope().close();274mappedBufferOp.apply(segment);275}276277@Test278public void testMappedSegmentOffset() throws Throwable {279File f = new File("test3.out");280f.createNewFile();281f.deleteOnExit();282283MemoryLayout tupleLayout = tuples.elementLayout();284285// write one at a time286for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {287try (ResourceScope scope = ResourceScope.newConfinedScope()) {288//write to channel289MemorySegment segment = MemorySegment.mapFile(f.toPath(), i, tuples.byteSize(), FileChannel.MapMode.READ_WRITE, scope);290initTuples(segment, 1);291segment.force();292}293}294295// check one at a time296for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {297try (ResourceScope scope = ResourceScope.newConfinedScope()) {298//read from channel299MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY, scope);300checkTuples(segment, segment.asByteBuffer(), 1);301}302}303}304305static final long LARGE_SIZE = 3L * 1024L * 1024L * 1024L; // 3GB306307@Test308public void testLargeMappedSegment() throws Throwable {309if (System.getProperty("sun.arch.data.model").equals("32")) {310throw new SkipException("large mapped files not supported on 32-bit systems");311}312313File f = new File("testLargeMappedSegment.out");314f.createNewFile();315f.deleteOnExit();316317try (ResourceScope scope = ResourceScope.newConfinedScope()) {318MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, LARGE_SIZE, FileChannel.MapMode.READ_WRITE, scope);319segment.isLoaded();320segment.load();321segment.isLoaded();322segment.force();323segment.isLoaded();324segment.unload();325segment.isLoaded();326}327}328329static void withMappedBuffer(FileChannel channel, FileChannel.MapMode mode, long pos, long size, Consumer<MappedByteBuffer> action) throws Throwable {330MappedByteBuffer mbb = channel.map(mode, pos, size);331var ref = new WeakReference<>(mbb);332action.accept(mbb);333mbb = null;334//wait for it to be GCed335System.gc();336while (ref.get() != null) {337Thread.sleep(20);338}339}340341static void checkByteArrayAlignment(MemoryLayout layout) {342if (layout.bitSize() > 32343&& System.getProperty("sun.arch.data.model").equals("32")) {344throw new SkipException("avoid unaligned access on 32-bit system");345}346}347348@Test(dataProvider = "bufferOps")349public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {350Buffer bb;351try (ResourceScope scope = ResourceScope.newConfinedScope()) {352MemorySegment segment = MemorySegment.allocateNative(bytes, scope);353bb = bufferFactory.apply(segment.asByteBuffer());354}355//outside of scope!!356try {357method.invoke(bb, args);358fail("Exception expected");359} catch (InvocationTargetException ex) {360Throwable cause = ex.getCause();361if (cause instanceof IllegalStateException) {362//all get/set buffer operation should fail because of the scope check363assertTrue(ex.getCause().getMessage().contains("Already closed"));364} else {365//all other exceptions were unexpected - fail366fail("Unexpected exception", cause);367}368} catch (Throwable ex) {369//unexpected exception - fail370fail("Unexpected exception", ex);371}372}373374@Test(dataProvider = "bufferHandleOps")375public void testScopedBufferAndVarHandle(VarHandle bufferHandle) {376ByteBuffer bb;377try (ResourceScope scope = ResourceScope.newConfinedScope()) {378MemorySegment segment = MemorySegment.allocateNative(bytes, scope);379bb = segment.asByteBuffer();380for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {381MethodHandle handle = e.getKey().bindTo(bufferHandle)382.asSpreader(Object[].class, e.getValue().length);383try {384handle.invoke(e.getValue());385} catch (UnsupportedOperationException ex) {386//skip387} catch (Throwable ex) {388//should not fail - segment is alive!389fail();390}391}392}393for (Map.Entry<MethodHandle, Object[]> e : varHandleMembers(bb, bufferHandle).entrySet()) {394try {395MethodHandle handle = e.getKey().bindTo(bufferHandle)396.asSpreader(Object[].class, e.getValue().length);397handle.invoke(e.getValue());398fail();399} catch (IllegalStateException ex) {400assertTrue(ex.getMessage().contains("Already closed"));401} catch (UnsupportedOperationException ex) {402//skip403} catch (Throwable ex) {404fail();405}406}407}408409@Test(dataProvider = "bufferOps")410public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {411try (ResourceScope scope = ResourceScope.newConfinedScope()) {412MemorySegment segment = MemorySegment.allocateNative(bytes, scope);413Buffer bb = bufferFactory.apply(segment.asByteBuffer());414assertTrue(bb.isDirect());415DirectBuffer directBuffer = ((DirectBuffer)bb);416assertEquals(directBuffer.address(), segment.address().toRawLongValue());417assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));418assertTrue(directBuffer.cleaner() == null);419}420}421422@Test(dataProvider="resizeOps")423public void testResizeOffheap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {424try (ResourceScope scope = ResourceScope.newConfinedScope()) {425MemorySegment segment = MemorySegment.allocateNative(seq, scope);426initializer.accept(segment);427checker.accept(segment);428}429}430431@Test(dataProvider="resizeOps")432public void testResizeHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {433checkByteArrayAlignment(seq.elementLayout());434int capacity = (int)seq.byteSize();435MemorySegment base = MemorySegment.ofArray(new byte[capacity]);436initializer.accept(base);437checker.accept(base);438}439440@Test(dataProvider="resizeOps")441public void testResizeBuffer(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {442checkByteArrayAlignment(seq.elementLayout());443int capacity = (int)seq.byteSize();444MemorySegment base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity]));445initializer.accept(base);446checker.accept(base);447}448449@Test(dataProvider="resizeOps")450public void testResizeRoundtripHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {451checkByteArrayAlignment(seq.elementLayout());452int capacity = (int)seq.byteSize();453byte[] arr = new byte[capacity];454MemorySegment segment = MemorySegment.ofArray(arr);455initializer.accept(segment);456MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer());457checker.accept(second);458}459460@Test(dataProvider="resizeOps")461public void testResizeRoundtripNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {462try (ResourceScope scope = ResourceScope.newConfinedScope()) {463MemorySegment segment = MemorySegment.allocateNative(seq, scope);464initializer.accept(segment);465MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer());466checker.accept(second);467}468}469470@Test(expectedExceptions = IllegalStateException.class)471public void testBufferOnClosedScope() {472MemorySegment leaked;473try (ResourceScope scope = ResourceScope.newConfinedScope()) {474leaked = MemorySegment.allocateNative(bytes, scope);475}476ByteBuffer byteBuffer = leaked.asByteBuffer(); // ok477byteBuffer.get(); // should throw478}479480@Test(expectedExceptions = IllegalStateException.class)481public void testTooBigForByteBuffer() {482MemorySegment segment = MemoryAddress.NULL.asSegment(Integer.MAX_VALUE + 10L, ResourceScope.globalScope());483segment.asByteBuffer();484}485486@Test(expectedExceptions = IllegalArgumentException.class)487public void testBadMapNegativeSize() throws IOException {488File f = new File("testNeg1.out");489f.createNewFile();490f.deleteOnExit();491MemorySegment.mapFile(f.toPath(), 0L, -1, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());492}493494@Test(expectedExceptions = IllegalArgumentException.class)495public void testBadMapNegativeOffset() throws IOException {496File f = new File("testNeg2.out");497f.createNewFile();498f.deleteOnExit();499MemorySegment.mapFile(f.toPath(), -1, 1, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());500}501502@Test503public void testMapOffset() throws IOException {504File f = new File("testMapOffset.out");505f.createNewFile();506f.deleteOnExit();507508int SIZE = Byte.MAX_VALUE;509510try (ResourceScope scope = ResourceScope.newConfinedScope()) {511MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0, SIZE, FileChannel.MapMode.READ_WRITE, scope);512for (byte offset = 0; offset < SIZE; offset++) {513MemoryAccess.setByteAtOffset(segment, offset, offset);514}515segment.force();516}517518for (int offset = 0 ; offset < SIZE ; offset++) {519try (ResourceScope scope = ResourceScope.newConfinedScope()) {520MemorySegment segment = MemorySegment.mapFile(f.toPath(), offset, SIZE - offset, FileChannel.MapMode.READ_ONLY, scope);521assertEquals(MemoryAccess.getByte(segment), offset);522}523}524}525526@Test527public void testMapZeroSize() throws IOException {528File f = new File("testPos1.out");529f.createNewFile();530f.deleteOnExit();531//RW532try (ResourceScope scope = ResourceScope.newConfinedScope()) {533MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_WRITE, scope);534assertEquals(segment.byteSize(), 0);535assertEquals(segment.isMapped(), true);536assertFalse(segment.isReadOnly());537segment.force();538segment.load();539segment.isLoaded();540segment.unload();541}542//RO543try (ResourceScope scope = ResourceScope.newConfinedScope()) {544MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_ONLY, scope);545assertEquals(segment.byteSize(), 0);546assertEquals(segment.isMapped(), true);547assertTrue(segment.isReadOnly());548segment.force();549segment.load();550segment.isLoaded();551segment.unload();552}553}554555@Test(expectedExceptions = IllegalArgumentException.class)556public void testMapCustomPath() throws IOException {557Path path = Path.of(URI.create("jrt:/"));558MemorySegment.mapFile(path, 0L, 0L, FileChannel.MapMode.READ_WRITE, ResourceScope.newImplicitScope());559}560561@Test(dataProvider="resizeOps")562public void testCopyHeapToNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {563checkByteArrayAlignment(seq.elementLayout());564int bytes = (int)seq.byteSize();565try (ResourceScope scope = ResourceScope.newConfinedScope()) {566MemorySegment nativeArray = MemorySegment.allocateNative(bytes, 1, scope);567MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);568initializer.accept(heapArray);569nativeArray.copyFrom(heapArray);570checker.accept(nativeArray);571}572}573574@Test(dataProvider="resizeOps")575public void testCopyNativeToHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {576checkByteArrayAlignment(seq.elementLayout());577int bytes = (int)seq.byteSize();578try (ResourceScope scope = ResourceScope.newConfinedScope()) {579MemorySegment nativeArray = MemorySegment.allocateNative(seq, scope);580MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes]);581initializer.accept(nativeArray);582heapArray.copyFrom(nativeArray);583checker.accept(heapArray);584}585}586587@Test588public void testDefaultAccessModesOfBuffer() {589ByteBuffer rwBuffer = ByteBuffer.wrap(new byte[4]);590{591MemorySegment segment = MemorySegment.ofByteBuffer(rwBuffer);592assertFalse(segment.isReadOnly());593}594595{596ByteBuffer roBuffer = rwBuffer.asReadOnlyBuffer();597MemorySegment segment = MemorySegment.ofByteBuffer(roBuffer);598assertTrue(segment.isReadOnly());599}600}601602@Test(dataProvider="bufferSources")603public void testBufferToSegment(ByteBuffer bb, Predicate<MemorySegment> segmentChecker) {604MemorySegment segment = MemorySegment.ofByteBuffer(bb);605assertEquals(segment.isReadOnly(), bb.isReadOnly());606assertTrue(segmentChecker.test(segment));607assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));608assertEquals(bb.capacity(), segment.byteSize());609//another round trip610segment = MemorySegment.ofByteBuffer(segment.asByteBuffer());611assertEquals(segment.isReadOnly(), bb.isReadOnly());612assertTrue(segmentChecker.test(segment));613assertTrue(segmentChecker.test(segment.asSlice(0, segment.byteSize())));614assertEquals(bb.capacity(), segment.byteSize());615}616617@Test(dataProvider="bufferSources")618public void bufferProperties(ByteBuffer bb, Predicate<MemorySegment> _unused) {619MemorySegment segment = MemorySegment.ofByteBuffer(bb);620ByteBuffer buffer = segment.asByteBuffer();621assertEquals(buffer.position(), 0);622assertEquals(buffer.capacity(), segment.byteSize());623assertEquals(buffer.limit(), segment.byteSize());624}625626@Test627public void testRoundTripAccess() {628try (ResourceScope scope = ResourceScope.newConfinedScope()) {629MemorySegment ms = MemorySegment.allocateNative(4, 1, scope);630MemorySegment msNoAccess = ms.asReadOnly();631MemorySegment msRoundTrip = MemorySegment.ofByteBuffer(msNoAccess.asByteBuffer());632assertEquals(msNoAccess.isReadOnly(), msRoundTrip.isReadOnly());633}634}635636@Test(expectedExceptions = IllegalStateException.class)637public void testDeadAccessOnClosedBufferSegment() {638MemorySegment s1 = MemorySegment.allocateNative(MemoryLayouts.JAVA_INT, ResourceScope.newConfinedScope());639MemorySegment s2 = MemorySegment.ofByteBuffer(s1.asByteBuffer());640641// memory freed642s1.scope().close();643644MemoryAccess.setInt(s2, 10); // Dead access!645}646647@Test(dataProvider = "allScopes")648public void testIOOnSegmentBuffer(Supplier<ResourceScope> scopeSupplier) throws IOException {649File tmp = File.createTempFile("tmp", "txt");650tmp.deleteOnExit();651ResourceScope scope;652try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE) ;653ResourceScope scp = closeableScopeOrNull(scope = scopeSupplier.get())) {654MemorySegment segment = MemorySegment.allocateNative(10, 1, scope);655for (int i = 0; i < 10; i++) {656MemoryAccess.setByteAtOffset(segment, i, (byte) i);657}658ByteBuffer bb = segment.asByteBuffer();659assertEquals(channel.write(bb), 10);660segment.fill((byte)0x00);661assertEquals(bb.clear(), ByteBuffer.wrap(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));662assertEquals(channel.position(0).read(bb.clear()), 10);663assertEquals(bb.flip(), ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));664}665}666667static final Class<IllegalStateException> ISE = IllegalStateException.class;668669@Test(dataProvider = "closeableScopes")670public void testIOOnClosedSegmentBuffer(Supplier<ResourceScope> scopeSupplier) throws IOException {671File tmp = File.createTempFile("tmp", "txt");672tmp.deleteOnExit();673try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {674MemorySegment segment = MemorySegment.allocateNative(10, scopeSupplier.get());675for (int i = 0; i < 10; i++) {676MemoryAccess.setByteAtOffset(segment, i, (byte) i);677}678ByteBuffer bb = segment.asByteBuffer();679segment.scope().close();680assertThrows(ISE, () -> channel.read(bb));681assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}));682assertThrows(ISE, () -> channel.read(new ByteBuffer[] {bb}, 0, 1));683assertThrows(ISE, () -> channel.write(bb));684assertThrows(ISE, () -> channel.write(new ByteBuffer[] {bb}));685assertThrows(ISE, () -> channel.write(new ByteBuffer[] {bb}, 0 ,1));686}687}688689@Test690public void buffersAndArraysFromSlices() {691try (ResourceScope scope = ResourceScope.newSharedScope()) {692MemorySegment segment = MemorySegment.allocateNative(16, scope);693int newSize = 8;694var slice = segment.asSlice(4, newSize);695696var bytes = slice.toByteArray();697assertEquals(newSize, bytes.length);698699var buffer = slice.asByteBuffer();700// Fails for heap segments, but passes for native segments:701assertEquals(0, buffer.position());702assertEquals(newSize, buffer.limit());703assertEquals(newSize, buffer.capacity());704}705}706707@Test708public void viewsFromSharedSegment() {709try (ResourceScope scope = ResourceScope.newSharedScope()) {710MemorySegment segment = MemorySegment.allocateNative(16, scope);711var byteBuffer = segment.asByteBuffer();712byteBuffer.asReadOnlyBuffer();713byteBuffer.slice(0, 8);714}715}716717@DataProvider(name = "segments")718public static Object[][] segments() throws Throwable {719return new Object[][] {720{ (Supplier<MemorySegment>) () -> MemorySegment.allocateNative(16, ResourceScope.newImplicitScope()) },721{ (Supplier<MemorySegment>) () -> MemorySegment.ofArray(new byte[16]) }722};723}724725@DataProvider(name = "closeableScopes")726public static Object[][] closeableScopes() {727return new Object[][] {728{ (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope() },729{ (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope() },730{ (Supplier<ResourceScope>) () -> ResourceScope.newSharedScope(Cleaner.create()) },731{ (Supplier<ResourceScope>) () -> ResourceScope.newConfinedScope(Cleaner.create()) }732};733}734735@DataProvider(name = "implicitScopes")736public static Object[][] implicitScopes() {737return new Object[][] {738{ (Supplier<ResourceScope>) ResourceScope::newImplicitScope },739{ (Supplier<ResourceScope>) ResourceScope::globalScope },740};741}742743@DataProvider(name = "allScopes")744public static Object[][] allScopes() {745return Stream.of(implicitScopes(), closeableScopes())746.flatMap(Arrays::stream)747.toArray(Object[][]::new);748}749750static ResourceScope closeableScopeOrNull(ResourceScope scope) {751if (scope.isImplicit())752return null;753return scope;754}755756@DataProvider(name = "bufferOps")757public static Object[][] bufferOps() throws Throwable {758List<Object[]> args = new ArrayList<>();759bufferOpsArgs(args, bb -> bb, ByteBuffer.class);760bufferOpsArgs(args, ByteBuffer::asCharBuffer, CharBuffer.class);761bufferOpsArgs(args, ByteBuffer::asShortBuffer, ShortBuffer.class);762bufferOpsArgs(args, ByteBuffer::asIntBuffer, IntBuffer.class);763bufferOpsArgs(args, ByteBuffer::asFloatBuffer, FloatBuffer.class);764bufferOpsArgs(args, ByteBuffer::asLongBuffer, LongBuffer.class);765bufferOpsArgs(args, ByteBuffer::asDoubleBuffer, DoubleBuffer.class);766return args.toArray(Object[][]::new);767}768769static void bufferOpsArgs(List<Object[]> argsList, Function<ByteBuffer, Buffer> factory, Class<?> bufferClass) {770for (Method m : bufferClass.getMethods()) {771//skip statics and method declared in j.l.Object772if (m.getDeclaringClass().equals(Object.class)773|| ((m.getModifiers() & Modifier.STATIC) != 0)774|| (!m.getName().contains("get") && !m.getName().contains("put"))775|| m.getParameterCount() > 2) continue;776Object[] args = Stream.of(m.getParameterTypes())777.map(TestByteBuffer::defaultValue)778.toArray();779argsList.add(new Object[] { factory, m, args });780}781}782783@DataProvider(name = "bufferHandleOps")784public static Object[][] bufferHandleOps() throws Throwable {785return new Object[][]{786{ MethodHandles.byteBufferViewVarHandle(char[].class, ByteOrder.nativeOrder()) },787{ MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()) },788{ MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder()) },789{ MethodHandles.byteBufferViewVarHandle(long[].class, ByteOrder.nativeOrder()) },790{ MethodHandles.byteBufferViewVarHandle(float[].class, ByteOrder.nativeOrder()) },791{ MethodHandles.byteBufferViewVarHandle(double[].class, ByteOrder.nativeOrder()) }792};793}794795static Map<MethodHandle, Object[]> varHandleMembers(ByteBuffer bb, VarHandle handle) {796Map<MethodHandle, Object[]> members = new HashMap<>();797for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {798Class<?>[] params = handle.accessModeType(mode).parameterArray();799Object[] args = Stream.concat(Stream.of(bb), Stream.of(params).skip(1)800.map(TestByteBuffer::defaultValue))801.toArray();802try {803members.put(MethodHandles.varHandleInvoker(mode, handle.accessModeType(mode)), args);804} catch (Throwable ex) {805throw new AssertionError(ex);806}807}808return members;809}810811@DataProvider(name = "resizeOps")812public Object[][] resizeOps() {813Consumer<MemorySegment> byteInitializer =814(base) -> initBytes(base, bytes, (addr, pos) -> MemoryAccess.setByteAtOffset(addr, pos, (byte)(long)pos));815Consumer<MemorySegment> charInitializer =816(base) -> initBytes(base, chars, (addr, pos) -> MemoryAccess.setCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (char)(long)pos));817Consumer<MemorySegment> shortInitializer =818(base) -> initBytes(base, shorts, (addr, pos) -> MemoryAccess.setShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (short)(long)pos));819Consumer<MemorySegment> intInitializer =820(base) -> initBytes(base, ints, (addr, pos) -> MemoryAccess.setIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (int)(long)pos));821Consumer<MemorySegment> floatInitializer =822(base) -> initBytes(base, floats, (addr, pos) -> MemoryAccess.setFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (float)(long)pos));823Consumer<MemorySegment> longInitializer =824(base) -> initBytes(base, longs, (addr, pos) -> MemoryAccess.setLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (long)pos));825Consumer<MemorySegment> doubleInitializer =826(base) -> initBytes(base, doubles, (addr, pos) -> MemoryAccess.setDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (double)(long)pos));827828Consumer<MemorySegment> byteChecker =829(base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> MemoryAccess.getByteAtOffset(addr, pos), ByteBuffer::get);830Consumer<MemorySegment> charChecker =831(base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> MemoryAccess.getCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), CharBuffer::get);832Consumer<MemorySegment> shortChecker =833(base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> MemoryAccess.getShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), ShortBuffer::get);834Consumer<MemorySegment> intChecker =835(base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> MemoryAccess.getIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), IntBuffer::get);836Consumer<MemorySegment> floatChecker =837(base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> MemoryAccess.getFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), FloatBuffer::get);838Consumer<MemorySegment> longChecker =839(base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> MemoryAccess.getLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), LongBuffer::get);840Consumer<MemorySegment> doubleChecker =841(base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> MemoryAccess.getDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), DoubleBuffer::get);842843return new Object[][]{844{byteChecker, byteInitializer, bytes},845{charChecker, charInitializer, chars},846{shortChecker, shortInitializer, shorts},847{intChecker, intInitializer, ints},848{floatChecker, floatInitializer, floats},849{longChecker, longInitializer, longs},850{doubleChecker, doubleInitializer, doubles}851};852}853854static Object defaultValue(Class<?> c) {855if (c.isPrimitive()) {856if (c == char.class) {857return (char)0;858} else if (c == boolean.class) {859return false;860} else if (c == byte.class) {861return (byte)0;862} else if (c == short.class) {863return (short)0;864} else if (c == int.class) {865return 0;866} else if (c == long.class) {867return 0L;868} else if (c == float.class) {869return 0f;870} else if (c == double.class) {871return 0d;872} else {873throw new IllegalStateException();874}875} else if (c.isArray()) {876if (c == char[].class) {877return new char[1];878} else if (c == boolean[].class) {879return new boolean[1];880} else if (c == byte[].class) {881return new byte[1];882} else if (c == short[].class) {883return new short[1];884} else if (c == int[].class) {885return new int[1];886} else if (c == long[].class) {887return new long[1];888} else if (c == float[].class) {889return new float[1];890} else if (c == double[].class) {891return new double[1];892} else {893throw new IllegalStateException();894}895} else if (c == String.class) {896return "asdf";897} else if (c == ByteBuffer.class) {898return ByteBuffer.wrap(new byte[1]);899} else if (c == CharBuffer.class) {900return CharBuffer.wrap(new char[1]);901} else if (c == ShortBuffer.class) {902return ShortBuffer.wrap(new short[1]);903} else if (c == IntBuffer.class) {904return IntBuffer.wrap(new int[1]);905} else if (c == FloatBuffer.class) {906return FloatBuffer.wrap(new float[1]);907} else if (c == LongBuffer.class) {908return LongBuffer.wrap(new long[1]);909} else if (c == DoubleBuffer.class) {910return DoubleBuffer.wrap(new double[1]);911} else {912return null;913}914}915916@DataProvider(name = "bufferSources")917public static Object[][] bufferSources() {918Predicate<MemorySegment> heapTest = segment -> segment instanceof HeapMemorySegmentImpl;919Predicate<MemorySegment> nativeTest = segment -> segment instanceof NativeMemorySegmentImpl;920Predicate<MemorySegment> mappedTest = segment -> segment instanceof MappedMemorySegmentImpl;921try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.READ, StandardOpenOption.WRITE)) {922return new Object[][]{923{ ByteBuffer.wrap(new byte[256]), heapTest },924{ ByteBuffer.allocate(256), heapTest },925{ ByteBuffer.allocateDirect(256), nativeTest },926{ channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256), mappedTest },927928{ ByteBuffer.wrap(new byte[256]).asReadOnlyBuffer(), heapTest },929{ ByteBuffer.allocate(256).asReadOnlyBuffer(), heapTest },930{ ByteBuffer.allocateDirect(256).asReadOnlyBuffer(), nativeTest },931{ channel.map(FileChannel.MapMode.READ_WRITE, 0L, 256).asReadOnlyBuffer(),932nativeTest /* this seems to be an existing bug in the BB implementation */ }933};934} catch (IOException ex) {935throw new ExceptionInInitializerError(ex);936}937}938939enum MappedSegmentOp {940LOAD(MemorySegment::load),941UNLOAD(MemorySegment::unload),942IS_LOADED(MemorySegment::isLoaded),943FORCE(MemorySegment::force),944BUFFER_LOAD(m -> ((MappedByteBuffer)m.asByteBuffer()).load()),945BUFFER_IS_LOADED(m -> ((MappedByteBuffer)m.asByteBuffer()).isLoaded()),946BUFFER_FORCE(m -> ((MappedByteBuffer)m.asByteBuffer()).force());947948949private Consumer<MemorySegment> segmentOp;950951MappedSegmentOp(Consumer<MemorySegment> segmentOp) {952this.segmentOp = segmentOp;953}954955void apply(MemorySegment segment) {956segmentOp.accept(segment);957}958}959960@DataProvider(name = "mappedOps")961public static Object[][] mappedOps() {962return Stream.of(MappedSegmentOp.values())963.map(op -> new Object[] { op })964.toArray(Object[][]::new);965}966}967968969