1 /* 2 * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @enablePreview 27 * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance 28 */ 29 30 import java.lang.foreign.MemoryLayout; 31 import java.lang.foreign.MemorySegment; 32 import java.lang.foreign.Arena; 33 import java.lang.foreign.ValueLayout; 34 import java.nio.ByteBuffer; 35 import java.nio.ByteOrder; 36 37 import org.testng.annotations.*; 38 import org.testng.SkipException; 39 import static org.testng.Assert.*; 40 41 public class TestMemoryAccessInstance { 42 43 static class Accessor<X, L extends ValueLayout> { 44 45 interface SegmentGetter<X, L> { 46 X get(MemorySegment segment, L layout, long offset); 47 } 48 49 interface SegmentSetter<X, L> { 50 void set(MemorySegment segment, L layout, long offset, X o); 51 } 52 53 interface BufferGetter<X> { 54 X get(ByteBuffer segment, int offset); 55 } 56 57 interface BufferSetter<X> { 58 void set(ByteBuffer buffer, int offset, X o); 59 } 60 61 final X value; 62 final L layout; 63 final SegmentGetter<X, L> segmentGetter; 64 final SegmentSetter<X, L> segmentSetter; 65 final BufferGetter<X> bufferGetter; 66 final BufferSetter<X> bufferSetter; 67 68 Accessor(L layout, X value, 69 SegmentGetter<X, L> segmentGetter, SegmentSetter<X, L> segmentSetter, 70 BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) { 71 this.layout = layout; 72 this.value = value; 73 this.segmentGetter = segmentGetter; 74 this.segmentSetter = segmentSetter; 75 this.bufferGetter = bufferGetter; 76 this.bufferSetter = bufferSetter; 77 } 78 79 void test() { 80 try (Arena arena = Arena.ofConfined()) { 81 MemorySegment segment = arena.allocate(128, 1); 82 ByteBuffer buffer = segment.asByteBuffer(); 83 segmentSetter.set(segment, layout, 8, value); 84 assertEquals(bufferGetter.get(buffer, 8), value); 85 bufferSetter.set(buffer, 8, value); 86 assertEquals(value, segmentGetter.get(segment, layout, 8)); 87 } 88 } 89 90 @SuppressWarnings("unchecked") 91 void testHyperAligned() { 92 try (Arena arena = Arena.ofConfined()) { 93 MemorySegment segment = arena.allocate(64, 1); 94 L alignedLayout = (L)layout.withByteAlignment(layout.byteSize() * 2); 95 try { 96 segmentSetter.set(segment, alignedLayout, 0, value); 97 fail(); 98 } catch (IllegalArgumentException exception) { 99 assertTrue(exception.getMessage().contains("greater")); 100 } 101 try { 102 segmentGetter.get(segment, alignedLayout, 0); 103 fail(); 104 } catch (IllegalArgumentException exception) { 105 assertTrue(exception.getMessage().contains("greater")); 106 } 107 } 108 } 109 110 X get(MemorySegment segment, long offset) { 111 return segmentGetter.get(segment, layout, offset); 112 } 113 114 void set(MemorySegment segment, long offset, X value) { 115 segmentSetter.set(segment, layout, offset, value); 116 } 117 118 static <L extends ValueLayout, X> Accessor<X, L> of(L layout, X value, 119 SegmentGetter<X, L> segmentGetter, SegmentSetter<X, L> segmentSetter, 120 BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) { 121 return new Accessor<>(layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); 122 } 123 } 124 125 @Test(dataProvider = "segmentAccessors") 126 public void testSegmentAccess(String testName, Accessor<?, ?> accessor) { 127 accessor.test(); 128 } 129 130 @Test(dataProvider = "segmentAccessors") 131 public void testSegmentAccessHyper(String testName, Accessor<?, ?> accessor) { 132 if (testName.contains("index")) { 133 accessor.testHyperAligned(); 134 } else { 135 throw new SkipException("Skipping"); 136 } 137 } 138 139 @Test(expectedExceptions = IllegalArgumentException.class, 140 expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") 141 public void badHeapSegmentSet() { 142 long byteSize = ValueLayout.ADDRESS.byteSize(); 143 Arena scope = Arena.ofAuto(); 144 MemorySegment targetSegment = scope.allocate(byteSize, 1); 145 MemorySegment segment = MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); 146 targetSegment.set(ValueLayout.ADDRESS, 0, segment); // should throw 147 } 148 149 @Test(expectedExceptions = IllegalArgumentException.class, 150 expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") 151 public void badHeapSegmentSetAtIndex() { 152 long byteSize = ValueLayout.ADDRESS.byteSize(); 153 Arena scope = Arena.ofAuto(); 154 MemorySegment targetSegment = scope.allocate(byteSize, 1); 155 MemorySegment segment = MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); 156 targetSegment.setAtIndex(ValueLayout.ADDRESS, 0, segment); // should throw 157 } 158 159 @Test(dataProvider = "segmentAccessors") 160 public <X, L extends ValueLayout> void badAccessOverflowInIndexedAccess(String testName, Accessor<X, L> accessor) { 161 MemorySegment segment = MemorySegment.ofArray(new byte[100]); 162 if (testName.contains("/index") && accessor.layout.byteSize() > 1) { 163 assertThrows(IndexOutOfBoundsException.class, () -> accessor.get(segment, Long.MAX_VALUE)); 164 assertThrows(IndexOutOfBoundsException.class, () -> accessor.set(segment, Long.MAX_VALUE, accessor.value)); 165 } 166 } 167 168 static final ByteOrder NE = ByteOrder.nativeOrder(); 169 170 @DataProvider(name = "segmentAccessors") 171 static Object[][] segmentAccessors() { 172 return new Object[][]{ 173 174 {"byte", Accessor.of(ValueLayout.JAVA_BYTE, (byte) 42, 175 MemorySegment::get, MemorySegment::set, 176 ByteBuffer::get, ByteBuffer::put) 177 }, 178 {"boolean", Accessor.of(ValueLayout.JAVA_BOOLEAN, false, 179 MemorySegment::get, MemorySegment::set, 180 (bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0)) 181 }, 182 {"char", Accessor.of(ValueLayout.JAVA_CHAR, (char) 42, 183 MemorySegment::get, MemorySegment::set, 184 (bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v)) 185 }, 186 {"short", Accessor.of(ValueLayout.JAVA_SHORT, (short) 42, 187 MemorySegment::get, MemorySegment::set, 188 (bb, pos) -> bb.order(NE).getShort(pos), (bb, pos, v) -> bb.order(NE).putShort(pos, v)) 189 }, 190 {"int", Accessor.of(ValueLayout.JAVA_INT, 42, 191 MemorySegment::get, MemorySegment::set, 192 (bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v)) 193 }, 194 {"float", Accessor.of(ValueLayout.JAVA_FLOAT, 42f, 195 MemorySegment::get, MemorySegment::set, 196 (bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v)) 197 }, 198 {"long", Accessor.of(ValueLayout.JAVA_LONG, 42L, 199 MemorySegment::get, MemorySegment::set, 200 (bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v)) 201 }, 202 {"double", Accessor.of(ValueLayout.JAVA_DOUBLE, 42d, 203 MemorySegment::get, MemorySegment::set, 204 (bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v)) 205 }, 206 { "address", Accessor.of(ValueLayout.ADDRESS, MemorySegment.ofAddress(42), 207 MemorySegment::get, MemorySegment::set, 208 (bb, pos) -> { 209 ByteBuffer nb = bb.order(NE); 210 long addr = ValueLayout.ADDRESS.byteSize() == 8 ? 211 nb.getLong(pos) : nb.getInt(pos); 212 return MemorySegment.ofAddress(addr); 213 }, 214 (bb, pos, v) -> { 215 ByteBuffer nb = bb.order(NE); 216 if (ValueLayout.ADDRESS.byteSize() == 8) { 217 nb.putLong(pos, v.address()); 218 } else { 219 nb.putInt(pos, (int)v.address()); 220 } 221 }) 222 }, 223 224 {"byte/index", Accessor.of(ValueLayout.JAVA_BYTE, (byte) 42, 225 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 226 (bb, pos) -> bb.order(NE).get(pos), (bb, pos, v) -> bb.order(NE).put(pos, v)) 227 }, 228 {"boolean/index", Accessor.of(ValueLayout.JAVA_BOOLEAN, true, 229 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 230 (bb, pos) -> bb.order(NE).get(pos) != 0, (bb, pos, v) -> bb.order(NE).put(pos, (byte) (v ? 1 : 0))) 231 }, 232 {"char/index", Accessor.of(ValueLayout.JAVA_CHAR, (char) 42, 233 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 234 (bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v)) 235 }, 236 {"short/index", Accessor.of(ValueLayout.JAVA_SHORT, (short) 42, 237 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 238 (bb, pos) -> bb.order(NE).getShort(pos * 2), (bb, pos, v) -> bb.order(NE).putShort(pos * 2, v)) 239 }, 240 {"int/index", Accessor.of(ValueLayout.JAVA_INT, 42, 241 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 242 (bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v)) 243 }, 244 {"float/index", Accessor.of(ValueLayout.JAVA_FLOAT, 42f, 245 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 246 (bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v)) 247 }, 248 {"long/index", Accessor.of(ValueLayout.JAVA_LONG, 42L, 249 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 250 (bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v)) 251 }, 252 {"double/index", Accessor.of(ValueLayout.JAVA_DOUBLE, 42d, 253 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 254 (bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v)) 255 }, 256 { "address/index", Accessor.of(ValueLayout.ADDRESS, MemorySegment.ofAddress(42), 257 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 258 (bb, pos) -> { 259 ByteBuffer nb = bb.order(NE); 260 long addr = ValueLayout.ADDRESS.byteSize() == 8 ? 261 nb.getLong(pos * 8) : nb.getInt(pos * 4); 262 return MemorySegment.ofAddress(addr); 263 }, 264 (bb, pos, v) -> { 265 ByteBuffer nb = bb.order(NE); 266 if (ValueLayout.ADDRESS.byteSize() == 8) { 267 nb.putLong(pos * 8, v.address()); 268 } else { 269 nb.putInt(pos * 4, (int)v.address()); 270 } 271 }) 272 }, 273 }; 274 } 275 }