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 }