1 /* 2 * Copyright (c) 2019, 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 * @requires vm.bits == 64 27 * @run testng/othervm -Xmx4G -XX:MaxDirectMemorySize=1M --enable-native-access=ALL-UNNAMED TestSegments 28 */ 29 30 import java.lang.foreign.*; 31 32 import org.testng.annotations.DataProvider; 33 import org.testng.annotations.Test; 34 35 import java.lang.invoke.VarHandle; 36 import java.nio.ByteBuffer; 37 import java.util.List; 38 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.concurrent.atomic.AtomicReference; 40 import java.util.function.IntFunction; 41 import java.util.function.Supplier; 42 43 import static java.lang.foreign.ValueLayout.JAVA_INT; 44 import static org.testng.Assert.*; 45 46 public class TestSegments { 47 48 @Test(dataProvider = "badSizeAndAlignments", expectedExceptions = IllegalArgumentException.class) 49 public void testBadAllocateAlign(long size, long align) { 50 Arena.ofAuto().allocate(size, align); 51 } 52 53 @Test 54 public void testZeroLengthNativeSegment() { 55 try (Arena arena = Arena.ofConfined()) { 56 Arena session = arena; 57 var segment = session.allocate(0, 1); 58 assertEquals(segment.byteSize(), 0); 59 MemoryLayout seq = MemoryLayout.sequenceLayout(0, JAVA_INT); 60 segment = session.allocate(seq); 61 assertEquals(segment.byteSize(), 0); 62 assertEquals(segment.address() % seq.byteAlignment(), 0); 63 segment = session.allocate(0, 4); 64 assertEquals(segment.byteSize(), 0); 65 assertEquals(segment.address() % 4, 0); 66 MemorySegment rawAddress = MemorySegment.ofAddress(segment.address()); 67 assertEquals(rawAddress.byteSize(), 0); 68 assertEquals(rawAddress.address() % 4, 0); 69 } 70 } 71 72 @Test(expectedExceptions = { OutOfMemoryError.class, 73 IllegalArgumentException.class }) 74 public void testAllocateTooBig() { 75 Arena.ofAuto().allocate(Long.MAX_VALUE, 1); 76 } 77 78 @Test(expectedExceptions = OutOfMemoryError.class) 79 public void testNativeAllocationTooBig() { 80 Arena scope = Arena.ofAuto(); 81 MemorySegment segment = scope.allocate(1024L * 1024 * 8 * 2, 1); // 2M 82 } 83 84 @Test 85 public void testNativeSegmentIsZeroed() { 86 VarHandle byteHandle = ValueLayout.JAVA_BYTE.varHandle(); 87 try (Arena arena = Arena.ofConfined()) { 88 MemorySegment segment = arena.allocate(1000, 1); 89 for (long i = 0 ; i < segment.byteSize() ; i++) { 90 assertEquals(0, (byte)byteHandle.get(segment, i)); 91 } 92 } 93 } 94 95 @Test 96 public void testSlices() { 97 VarHandle byteHandle = ValueLayout.JAVA_BYTE.varHandle(); 98 try (Arena arena = Arena.ofConfined()) { 99 MemorySegment segment = arena.allocate(10, 1); 100 //init 101 for (byte i = 0 ; i < segment.byteSize() ; i++) { 102 byteHandle.set(segment, (long)i, i); 103 } 104 for (int offset = 0 ; offset < 10 ; offset++) { 105 MemorySegment slice = segment.asSlice(offset); 106 for (long i = offset ; i < 10 ; i++) { 107 assertEquals( 108 byteHandle.get(segment, i), 109 byteHandle.get(slice, i - offset) 110 ); 111 } 112 } 113 } 114 } 115 116 @Test(dataProvider = "segmentFactories") 117 public void testDerivedScopes(Supplier<MemorySegment> segmentSupplier) { 118 MemorySegment segment = segmentSupplier.get(); 119 assertEquals(segment.scope(), segment.scope()); 120 // one level 121 assertEquals(segment.asSlice(0).scope(), segment.scope()); 122 assertEquals(segment.asReadOnly().scope(), segment.scope()); 123 // two levels 124 assertEquals(segment.asSlice(0).asReadOnly().scope(), segment.scope()); 125 assertEquals(segment.asReadOnly().asSlice(0).scope(), segment.scope()); 126 // check fresh every time 127 MemorySegment another = segmentSupplier.get(); 128 assertNotEquals(segment.scope(), another.scope()); 129 } 130 131 @Test 132 public void testEqualsOffHeap() { 133 try (Arena arena = Arena.ofConfined()) { 134 Arena scope1 = arena; 135 MemorySegment segment = scope1.allocate(100, 1); 136 assertEquals(segment, segment.asReadOnly()); 137 assertEquals(segment, segment.asSlice(0, 100)); 138 assertNotEquals(segment, segment.asSlice(10, 90)); 139 assertEquals(segment, segment.asSlice(0, 90)); 140 assertEquals(segment, MemorySegment.ofAddress(segment.address())); 141 MemorySegment segment2 = arena.allocate(100, 1); 142 assertNotEquals(segment, segment2); 143 } 144 } 145 146 @Test 147 public void testEqualsOnHeap() { 148 MemorySegment segment = MemorySegment.ofArray(new byte[100]); 149 assertEquals(segment, segment.asReadOnly()); 150 assertEquals(segment, segment.asSlice(0, 100)); 151 assertNotEquals(segment, segment.asSlice(10, 90)); 152 assertEquals(segment, segment.asSlice(0, 90)); 153 MemorySegment segment2 = MemorySegment.ofArray(new byte[100]); 154 assertNotEquals(segment, segment2); 155 } 156 157 @Test 158 public void testHashCodeOffHeap() { 159 try (Arena arena = Arena.ofConfined()) { 160 MemorySegment segment = arena.allocate(100, 1); 161 assertEquals(segment.hashCode(), segment.asReadOnly().hashCode()); 162 assertEquals(segment.hashCode(), segment.asSlice(0, 100).hashCode()); 163 assertEquals(segment.hashCode(), segment.asSlice(0, 90).hashCode()); 164 assertEquals(segment.hashCode(), MemorySegment.ofAddress(segment.address()).hashCode()); 165 } 166 } 167 168 @Test 169 public void testHashCodeOnHeap() { 170 MemorySegment segment = MemorySegment.ofArray(new byte[100]); 171 assertEquals(segment.hashCode(), segment.asReadOnly().hashCode()); 172 assertEquals(segment.hashCode(), segment.asSlice(0, 100).hashCode()); 173 assertEquals(segment.hashCode(), segment.asSlice(0, 90).hashCode()); 174 } 175 176 @Test(expectedExceptions = IndexOutOfBoundsException.class) 177 public void testSmallSegmentMax() { 178 long offset = (long)Integer.MAX_VALUE + (long)Integer.MAX_VALUE + 2L + 6L; // overflows to 6 when cast to int 179 Arena scope = Arena.ofAuto(); 180 MemorySegment memorySegment = scope.allocate(10, 1); 181 memorySegment.get(JAVA_INT, offset); 182 } 183 184 @Test(expectedExceptions = IndexOutOfBoundsException.class) 185 public void testSmallSegmentMin() { 186 long offset = ((long)Integer.MIN_VALUE * 2L) + 6L; // underflows to 6 when cast to int 187 Arena scope = Arena.ofAuto(); 188 MemorySegment memorySegment = scope.allocate(10L, 1); 189 memorySegment.get(JAVA_INT, offset); 190 } 191 192 @Test 193 public void testSegmentOOBMessage() { 194 try { 195 var segment = Arena.global().allocate(10, 1); 196 segment.getAtIndex(ValueLayout.JAVA_INT, 2); 197 } catch (IndexOutOfBoundsException ex) { 198 assertTrue(ex.getMessage().contains("Out of bound access")); 199 assertTrue(ex.getMessage().contains("offset = 8")); 200 assertTrue(ex.getMessage().contains("length = 4")); 201 } 202 } 203 204 @Test(dataProvider = "segmentFactories") 205 public void testAccessModesOfFactories(Supplier<MemorySegment> segmentSupplier) { 206 MemorySegment segment = segmentSupplier.get(); 207 assertFalse(segment.isReadOnly()); 208 } 209 210 @DataProvider(name = "scopes") 211 public Object[][] scopes() { 212 return new Object[][] { 213 { Arena.ofAuto(), false }, 214 { Arena.global(), false }, 215 { Arena.ofConfined(), true }, 216 { Arena.ofShared(), false } 217 }; 218 } 219 220 @Test(dataProvider = "scopes") 221 public void testIsAccessibleBy(Arena arena, boolean isConfined) { 222 MemorySegment segment = MemorySegment.NULL.reinterpret(arena, null); 223 assertTrue(segment.isAccessibleBy(Thread.currentThread())); 224 assertTrue(segment.isAccessibleBy(new Thread()) != isConfined); 225 } 226 227 @Test(dataProvider = "segmentFactories") 228 public void testToString(Supplier<MemorySegment> segmentSupplier) { 229 var segment = segmentSupplier.get(); 230 String s = segment.toString(); 231 assertTrue(s.startsWith("MemorySegment{")); 232 assertTrue(s.contains("address: 0x")); 233 assertTrue(s.contains("byteSize: ")); 234 if (segment.heapBase().isPresent()) { 235 assertTrue(s.contains("heapBase: [")); 236 } else { 237 assertFalse(s.contains("heapBase: ")); 238 } 239 assertFalse(s.contains("Optional")); 240 } 241 242 @DataProvider(name = "segmentFactories") 243 public Object[][] segmentFactories() { 244 List<Supplier<MemorySegment>> l = List.of( 245 () -> MemorySegment.ofArray(new byte[] { 0x00, 0x01, 0x02, 0x03 }), 246 () -> MemorySegment.ofArray(new char[] {'a', 'b', 'c', 'd' }), 247 () -> MemorySegment.ofArray(new double[] { 1d, 2d, 3d, 4d} ), 248 () -> MemorySegment.ofArray(new float[] { 1.0f, 2.0f, 3.0f, 4.0f }), 249 () -> MemorySegment.ofArray(new int[] { 1, 2, 3, 4 }), 250 () -> MemorySegment.ofArray(new long[] { 1L, 2L, 3L, 4L } ), 251 () -> MemorySegment.ofArray(new short[] { 1, 2, 3, 4 } ), 252 () -> Arena.ofAuto().allocate(4L, 1), 253 () -> Arena.ofAuto().allocate(4L, 8), 254 () -> Arena.ofAuto().allocate(JAVA_INT), 255 () -> Arena.ofAuto().allocate(4L, 1), 256 () -> Arena.ofAuto().allocate(4L, 8), 257 () -> Arena.ofAuto().allocate(JAVA_INT) 258 259 ); 260 return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new); 261 } 262 263 @Test(dataProvider = "segmentFactories") 264 public void testFill(Supplier<MemorySegment> segmentSupplier) { 265 VarHandle byteHandle = ValueLayout.JAVA_BYTE.varHandle(); 266 267 for (byte value : new byte[] {(byte) 0xFF, (byte) 0x00, (byte) 0x45}) { 268 MemorySegment segment = segmentSupplier.get(); 269 segment.fill(value); 270 for (long l = 0; l < segment.byteSize(); l++) { 271 assertEquals((byte) byteHandle.get(segment, l), value); 272 } 273 274 // fill a slice 275 var sliceSegment = segment.asSlice(1, segment.byteSize() - 2).fill((byte) ~value); 276 for (long l = 0; l < sliceSegment.byteSize(); l++) { 277 assertEquals((byte) byteHandle.get(sliceSegment, l), ~value); 278 } 279 // assert enclosing slice 280 assertEquals((byte) byteHandle.get(segment, 0L), value); 281 for (long l = 1; l < segment.byteSize() - 2; l++) { 282 assertEquals((byte) byteHandle.get(segment, l), (byte) ~value); 283 } 284 assertEquals((byte) byteHandle.get(segment, segment.byteSize() - 1L), value); 285 } 286 } 287 288 @Test(dataProvider = "segmentFactories") 289 public void testHeapBase(Supplier<MemorySegment> segmentSupplier) { 290 MemorySegment segment = segmentSupplier.get(); 291 assertEquals(segment.isNative(), !segment.heapBase().isPresent()); 292 segment = segment.asReadOnly(); 293 assertTrue(segment.heapBase().isEmpty()); 294 } 295 296 @Test 297 public void testScopeConfinedArena() { 298 try (Arena arena = Arena.ofConfined()) { 299 MemorySegment segment = arena.allocate(100); 300 assertEquals(segment.scope(), arena.scope()); 301 } 302 } 303 304 @Test 305 public void testScopeSharedArena() { 306 try (Arena arena = Arena.ofShared()) { 307 MemorySegment segment = arena.allocate(100); 308 assertEquals(segment.scope(), arena.scope()); 309 } 310 } 311 312 @Test 313 public void testScopeAutoArena() { 314 Arena arena = Arena.ofAuto(); 315 MemorySegment segment = arena.allocate(100); 316 assertEquals(segment.scope(), arena.scope()); 317 } 318 319 @Test 320 public void testScopeGlobalArena() { 321 Arena arena = Arena.global(); 322 MemorySegment segment = arena.allocate(100); 323 assertEquals(segment.scope(), arena.scope()); 324 } 325 326 @Test(dataProvider = "segmentFactories", expectedExceptions = UnsupportedOperationException.class) 327 public void testFillIllegalAccessMode(Supplier<MemorySegment> segmentSupplier) { 328 MemorySegment segment = segmentSupplier.get(); 329 segment.asReadOnly().fill((byte) 0xFF); 330 } 331 332 @Test(dataProvider = "segmentFactories") 333 public void testFillThread(Supplier<MemorySegment> segmentSupplier) throws Exception { 334 MemorySegment segment = segmentSupplier.get(); 335 AtomicReference<RuntimeException> exception = new AtomicReference<>(); 336 Runnable action = () -> { 337 try { 338 segment.fill((byte) 0xBA); 339 } catch (RuntimeException e) { 340 exception.set(e); 341 } 342 }; 343 Thread thread = new Thread(action); 344 thread.start(); 345 thread.join(); 346 347 if (!segment.isAccessibleBy(Thread.currentThread())) { 348 RuntimeException e = exception.get(); 349 throw e; 350 } else { 351 assertNull(exception.get()); 352 } 353 } 354 355 @Test 356 public void testFillEmpty() { 357 MemorySegment.ofArray(new byte[] { }).fill((byte) 0xFF); 358 MemorySegment.ofArray(new byte[2]).asSlice(0, 0).fill((byte) 0xFF); 359 MemorySegment.ofBuffer(ByteBuffer.allocateDirect(0)).fill((byte) 0xFF); 360 } 361 362 @Test(dataProvider = "heapFactories") 363 public void testVirtualizedBaseAddress(IntFunction<MemorySegment> heapSegmentFactory, int factor) { 364 MemorySegment segment = heapSegmentFactory.apply(10); 365 assertEquals(segment.address(), 0); // base address should be zero (no leaking of impl details) 366 MemorySegment end = segment.asSlice(segment.byteSize(), 0); 367 assertEquals(end.address(), segment.byteSize()); // end address should be equal to segment byte size 368 } 369 370 @Test 371 void testReinterpret() { 372 AtomicInteger counter = new AtomicInteger(); 373 try (Arena arena = Arena.ofConfined()){ 374 // check size 375 assertEquals(MemorySegment.ofAddress(42).reinterpret(100).byteSize(), 100); 376 assertEquals(MemorySegment.ofAddress(42).reinterpret(100, Arena.ofAuto(), null).byteSize(), 100); 377 // check scope and cleanup 378 assertEquals(MemorySegment.ofAddress(42).reinterpret(100, arena, s -> counter.incrementAndGet()).scope(), arena.scope()); 379 assertEquals(MemorySegment.ofAddress(42).reinterpret(arena, s -> counter.incrementAndGet()).scope(), arena.scope()); 380 } 381 assertEquals(counter.get(), 2); 382 } 383 384 @DataProvider(name = "badSizeAndAlignments") 385 public Object[][] sizesAndAlignments() { 386 return new Object[][] { 387 { -1, 8 }, 388 { 1, 15 }, 389 { 1, -15 } 390 }; 391 } 392 393 @DataProvider(name = "heapFactories") 394 public Object[][] heapFactories() { 395 return new Object[][] { 396 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new byte[size]), 1 }, 397 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new char[size]), 2 }, 398 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new short[size]), 2 }, 399 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new int[size]), 4 }, 400 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new float[size]), 4 }, 401 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new long[size]), 8 }, 402 { (IntFunction<MemorySegment>) size -> MemorySegment.ofArray(new double[size]), 8 } 403 }; 404 } 405 }