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 * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryAccessInstance 27 */ 28 29 import java.lang.foreign.MemoryLayout; 30 import java.lang.foreign.MemorySegment; 31 import java.lang.foreign.Arena; 32 import java.lang.foreign.ValueLayout; 33 import java.nio.ByteBuffer; 34 import java.nio.ByteOrder; 35 36 import org.testng.annotations.*; 37 import org.testng.SkipException; 38 import static org.testng.Assert.*; 39 40 public class TestMemoryAccessInstance { 41 42 static class Accessor<X, L extends ValueLayout> { 43 44 interface SegmentGetter<X, L> { 45 X get(MemorySegment segment, L layout, long offset); 46 } 47 48 interface SegmentSetter<X, L> { 49 void set(MemorySegment segment, L layout, long offset, X o); 50 } 51 52 interface BufferGetter<X> { 53 X get(ByteBuffer segment, int offset); 54 } 55 56 interface BufferSetter<X> { 57 void set(ByteBuffer buffer, int offset, X o); 58 } 59 60 final X value; 61 final L layout; 62 final SegmentGetter<X, L> segmentGetter; 63 final SegmentSetter<X, L> segmentSetter; 64 final BufferGetter<X> bufferGetter; 65 final BufferSetter<X> bufferSetter; 66 67 Accessor(L layout, X value, 68 SegmentGetter<X, L> segmentGetter, SegmentSetter<X, L> segmentSetter, 69 BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) { 70 this.layout = layout; 71 this.value = value; 72 this.segmentGetter = segmentGetter; 73 this.segmentSetter = segmentSetter; 74 this.bufferGetter = bufferGetter; 75 this.bufferSetter = bufferSetter; 76 } 77 78 void test() { 79 try (Arena arena = Arena.ofConfined()) { 80 MemorySegment segment = arena.allocate(128, 1); 81 ByteBuffer buffer = segment.asByteBuffer(); 82 segmentSetter.set(segment, layout, 8, value); 83 assertEquals(bufferGetter.get(buffer, 8), value); 84 bufferSetter.set(buffer, 8, value); 85 assertEquals(value, segmentGetter.get(segment, layout, 8)); 86 } 87 } 88 89 @SuppressWarnings("unchecked") 90 void testHyperAligned() { 91 try (Arena arena = Arena.ofConfined()) { 92 MemorySegment segment = arena.allocate(64, 1); 93 L alignedLayout = (L)layout.withByteAlignment(layout.byteSize() * 2); 94 try { 95 segmentSetter.set(segment, alignedLayout, 0, value); 96 fail(); 97 } catch (IllegalArgumentException exception) { 98 assertTrue(exception.getMessage().contains("greater")); 99 } 100 try { 101 segmentGetter.get(segment, alignedLayout, 0); 102 fail(); 103 } catch (IllegalArgumentException exception) { 104 assertTrue(exception.getMessage().contains("greater")); 105 } 106 } 107 } 108 109 X get(MemorySegment segment, long offset) { 110 return segmentGetter.get(segment, layout, offset); 111 } 112 113 void set(MemorySegment segment, long offset, X value) { 114 segmentSetter.set(segment, layout, offset, value); 115 } 116 117 static <L extends ValueLayout, X> Accessor<X, L> of(L layout, X value, 118 SegmentGetter<X, L> segmentGetter, SegmentSetter<X, L> segmentSetter, 119 BufferGetter<X> bufferGetter, BufferSetter<X> bufferSetter) { 120 return new Accessor<>(layout, value, segmentGetter, segmentSetter, bufferGetter, bufferSetter); 121 } 122 } 123 124 @Test(dataProvider = "segmentAccessors") 125 public void testSegmentAccess(String testName, Accessor<?, ?> accessor) { 126 accessor.test(); 127 } 128 129 @Test(dataProvider = "segmentAccessors") 130 public void testSegmentAccessHyper(String testName, Accessor<?, ?> accessor) { 131 if (testName.contains("index")) { 132 accessor.testHyperAligned(); 133 } else { 134 throw new SkipException("Skipping"); 135 } 136 } 137 138 @Test(expectedExceptions = IllegalArgumentException.class, 139 expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") 140 public void badHeapSegmentSet() { 141 long byteSize = ValueLayout.ADDRESS.byteSize(); 142 Arena scope = Arena.ofAuto(); 143 MemorySegment targetSegment = scope.allocate(byteSize, 1); 144 MemorySegment segment = MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); 145 targetSegment.set(ValueLayout.ADDRESS, 0, segment); // should throw 146 } 147 148 @Test(expectedExceptions = IllegalArgumentException.class, 149 expectedExceptionsMessageRegExp = ".*Heap segment not allowed.*") 150 public void badHeapSegmentSetAtIndex() { 151 long byteSize = ValueLayout.ADDRESS.byteSize(); 152 Arena scope = Arena.ofAuto(); 153 MemorySegment targetSegment = scope.allocate(byteSize, 1); 154 MemorySegment segment = MemorySegment.ofArray(new byte[]{ 0, 1, 2 }); 155 targetSegment.setAtIndex(ValueLayout.ADDRESS, 0, segment); // should throw 156 } 157 158 @Test(dataProvider = "segmentAccessors") 159 public <X, L extends ValueLayout> void badAccessOverflowInIndexedAccess(String testName, Accessor<X, L> accessor) { 160 MemorySegment segment = MemorySegment.ofArray(new byte[100]); 161 if (testName.contains("/index") && accessor.layout.byteSize() > 1) { 162 assertThrows(IndexOutOfBoundsException.class, () -> accessor.get(segment, Long.MAX_VALUE)); 163 assertThrows(IndexOutOfBoundsException.class, () -> accessor.set(segment, Long.MAX_VALUE, accessor.value)); 164 } 165 } 166 167 static final ByteOrder NE = ByteOrder.nativeOrder(); 168 169 @DataProvider(name = "segmentAccessors") 170 static Object[][] segmentAccessors() { 171 return new Object[][]{ 172 173 {"byte", Accessor.of(ValueLayout.JAVA_BYTE, (byte) 42, 174 MemorySegment::get, MemorySegment::set, 175 ByteBuffer::get, ByteBuffer::put) 176 }, 177 {"boolean", Accessor.of(ValueLayout.JAVA_BOOLEAN, false, 178 MemorySegment::get, MemorySegment::set, 179 (bb, pos) -> bb.get(pos) != 0, (bb, pos, v) -> bb.put(pos, v ? (byte)1 : (byte)0)) 180 }, 181 {"char", Accessor.of(ValueLayout.JAVA_CHAR, (char) 42, 182 MemorySegment::get, MemorySegment::set, 183 (bb, pos) -> bb.order(NE).getChar(pos), (bb, pos, v) -> bb.order(NE).putChar(pos, v)) 184 }, 185 {"short", Accessor.of(ValueLayout.JAVA_SHORT, (short) 42, 186 MemorySegment::get, MemorySegment::set, 187 (bb, pos) -> bb.order(NE).getShort(pos), (bb, pos, v) -> bb.order(NE).putShort(pos, v)) 188 }, 189 {"int", Accessor.of(ValueLayout.JAVA_INT, 42, 190 MemorySegment::get, MemorySegment::set, 191 (bb, pos) -> bb.order(NE).getInt(pos), (bb, pos, v) -> bb.order(NE).putInt(pos, v)) 192 }, 193 {"float", Accessor.of(ValueLayout.JAVA_FLOAT, 42f, 194 MemorySegment::get, MemorySegment::set, 195 (bb, pos) -> bb.order(NE).getFloat(pos), (bb, pos, v) -> bb.order(NE).putFloat(pos, v)) 196 }, 197 {"long", Accessor.of(ValueLayout.JAVA_LONG, 42L, 198 MemorySegment::get, MemorySegment::set, 199 (bb, pos) -> bb.order(NE).getLong(pos), (bb, pos, v) -> bb.order(NE).putLong(pos, v)) 200 }, 201 {"double", Accessor.of(ValueLayout.JAVA_DOUBLE, 42d, 202 MemorySegment::get, MemorySegment::set, 203 (bb, pos) -> bb.order(NE).getDouble(pos), (bb, pos, v) -> bb.order(NE).putDouble(pos, v)) 204 }, 205 { "address", Accessor.of(ValueLayout.ADDRESS, MemorySegment.ofAddress(42), 206 MemorySegment::get, MemorySegment::set, 207 (bb, pos) -> { 208 ByteBuffer nb = bb.order(NE); 209 long addr = ValueLayout.ADDRESS.byteSize() == 8 ? 210 nb.getLong(pos) : nb.getInt(pos); 211 return MemorySegment.ofAddress(addr); 212 }, 213 (bb, pos, v) -> { 214 ByteBuffer nb = bb.order(NE); 215 if (ValueLayout.ADDRESS.byteSize() == 8) { 216 nb.putLong(pos, v.address()); 217 } else { 218 nb.putInt(pos, (int)v.address()); 219 } 220 }) 221 }, 222 223 {"byte/index", Accessor.of(ValueLayout.JAVA_BYTE, (byte) 42, 224 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 225 (bb, pos) -> bb.order(NE).get(pos), (bb, pos, v) -> bb.order(NE).put(pos, v)) 226 }, 227 {"boolean/index", Accessor.of(ValueLayout.JAVA_BOOLEAN, true, 228 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 229 (bb, pos) -> bb.order(NE).get(pos) != 0, (bb, pos, v) -> bb.order(NE).put(pos, (byte) (v ? 1 : 0))) 230 }, 231 {"char/index", Accessor.of(ValueLayout.JAVA_CHAR, (char) 42, 232 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 233 (bb, pos) -> bb.order(NE).getChar(pos * 2), (bb, pos, v) -> bb.order(NE).putChar(pos * 2, v)) 234 }, 235 {"short/index", Accessor.of(ValueLayout.JAVA_SHORT, (short) 42, 236 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 237 (bb, pos) -> bb.order(NE).getShort(pos * 2), (bb, pos, v) -> bb.order(NE).putShort(pos * 2, v)) 238 }, 239 {"int/index", Accessor.of(ValueLayout.JAVA_INT, 42, 240 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 241 (bb, pos) -> bb.order(NE).getInt(pos * 4), (bb, pos, v) -> bb.order(NE).putInt(pos * 4, v)) 242 }, 243 {"float/index", Accessor.of(ValueLayout.JAVA_FLOAT, 42f, 244 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 245 (bb, pos) -> bb.order(NE).getFloat(pos * 4), (bb, pos, v) -> bb.order(NE).putFloat(pos * 4, v)) 246 }, 247 {"long/index", Accessor.of(ValueLayout.JAVA_LONG, 42L, 248 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 249 (bb, pos) -> bb.order(NE).getLong(pos * 8), (bb, pos, v) -> bb.order(NE).putLong(pos * 8, v)) 250 }, 251 {"double/index", Accessor.of(ValueLayout.JAVA_DOUBLE, 42d, 252 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 253 (bb, pos) -> bb.order(NE).getDouble(pos * 8), (bb, pos, v) -> bb.order(NE).putDouble(pos * 8, v)) 254 }, 255 { "address/index", Accessor.of(ValueLayout.ADDRESS, MemorySegment.ofAddress(42), 256 MemorySegment::getAtIndex, MemorySegment::setAtIndex, 257 (bb, pos) -> { 258 ByteBuffer nb = bb.order(NE); 259 long addr = ValueLayout.ADDRESS.byteSize() == 8 ? 260 nb.getLong(pos * 8) : nb.getInt(pos * 4); 261 return MemorySegment.ofAddress(addr); 262 }, 263 (bb, pos, v) -> { 264 ByteBuffer nb = bb.order(NE); 265 if (ValueLayout.ADDRESS.byteSize() == 8) { 266 nb.putLong(pos * 8, v.address()); 267 } else { 268 nb.putInt(pos * 4, (int)v.address()); 269 } 270 }) 271 }, 272 }; 273 } 274 }