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 * @run testng TestLayouts 27 */ 28 29 import java.lang.foreign.*; 30 31 import java.lang.invoke.VarHandle; 32 import java.nio.ByteOrder; 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.function.LongFunction; 36 import java.util.stream.Stream; 37 38 import org.testng.annotations.*; 39 40 import static java.lang.foreign.ValueLayout.*; 41 import static org.testng.Assert.*; 42 43 public class TestLayouts { 44 45 @Test(dataProvider = "badAlignments", expectedExceptions = IllegalArgumentException.class) 46 public void testBadLayoutAlignment(MemoryLayout layout, long alignment) { 47 layout.withByteAlignment(alignment); 48 } 49 50 @Test(dataProvider = "basicLayoutsAndAddressAndGroups") 51 public void testEqualities(MemoryLayout layout) { 52 53 // Use another Type 54 MemoryLayout differentType = MemoryLayout.paddingLayout(1); 55 assertFalse(layout.equals(differentType)); 56 57 // Use another name 58 MemoryLayout differentName = layout.withName("CustomName"); 59 assertFalse(layout.equals(differentName)); 60 61 // Use another alignment 62 MemoryLayout differentAlignment = layout.withByteAlignment(layout.byteAlignment() * 2); 63 assertFalse(layout.equals(differentAlignment)); 64 65 // Swap endian 66 MemoryLayout differentOrder = JAVA_INT.withOrder(JAVA_INT.order() == ByteOrder.BIG_ENDIAN ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); 67 assertFalse(layout.equals(differentOrder)); 68 69 // Something totally different 70 assertFalse(layout.equals("A")); 71 72 // Null 73 assertFalse(layout.equals(null)); 74 75 // Identity 76 assertTrue(layout.equals(layout)); 77 78 assertFalse(layout.equals(MemoryLayout.sequenceLayout(13, JAVA_LONG))); 79 80 MemoryLayout other = layout.withByteAlignment(16).withByteAlignment(layout.byteAlignment()); 81 assertTrue(layout.equals(other)); 82 83 } 84 85 public void testTargetLayoutEquals() { 86 MemoryLayout differentTargetLayout = ADDRESS.withTargetLayout(JAVA_CHAR); 87 assertFalse(ADDRESS.equals(differentTargetLayout)); 88 var equalButNotSame = ADDRESS.withTargetLayout(JAVA_INT).withTargetLayout(JAVA_CHAR); 89 assertTrue(differentTargetLayout.equals(equalButNotSame)); 90 } 91 92 @Test 93 public void testIndexedSequencePath() { 94 MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT); 95 try (Arena arena = Arena.ofConfined()) { 96 MemorySegment segment = arena.allocate(seq);; 97 VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement()); 98 // init segment 99 for (int i = 0 ; i < 10 ; i++) { 100 indexHandle.set(segment, 0L, (long)i, i); 101 } 102 //check statically indexed handles 103 for (int i = 0 ; i < 10 ; i++) { 104 VarHandle preindexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement(i)); 105 int expected = (int)indexHandle.get(segment, 0L, (long)i); 106 int found = (int)preindexHandle.get(segment, 0L); 107 assertEquals(expected, found); 108 } 109 } 110 } 111 112 @Test(expectedExceptions = IllegalArgumentException.class) 113 public void testBadBoundSequenceLayoutResize() { 114 SequenceLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT); 115 seq.withElementCount(-1); 116 } 117 118 @Test(expectedExceptions = IllegalArgumentException.class) 119 public void testReshape() { 120 SequenceLayout layout = MemoryLayout.sequenceLayout(10, JAVA_INT); 121 layout.reshape(); 122 } 123 124 @Test(dataProvider = "basicLayoutsAndAddressAndGroups", expectedExceptions = IllegalArgumentException.class) 125 public void testGroupIllegalAlignmentNotPowerOfTwo(MemoryLayout layout) { 126 layout.withByteAlignment(9); 127 } 128 129 @Test(dataProvider = "basicLayoutsAndAddressAndGroups", expectedExceptions = IllegalArgumentException.class) 130 public void testGroupIllegalAlignmentNotGreaterOrEqualTo1(MemoryLayout layout) { 131 layout.withByteAlignment(0); 132 } 133 134 @Test 135 public void testEqualsPadding() { 136 PaddingLayout paddingLayout = MemoryLayout.paddingLayout(2); 137 testEqualities(paddingLayout); 138 PaddingLayout paddingLayout2 = MemoryLayout.paddingLayout(4); 139 assertNotEquals(paddingLayout, paddingLayout2); 140 } 141 142 @Test 143 public void testEmptyGroup() { 144 MemoryLayout struct = MemoryLayout.structLayout(); 145 assertEquals(struct.byteSize(), 0); 146 assertEquals(struct.byteAlignment(), 1); 147 148 MemoryLayout union = MemoryLayout.unionLayout(); 149 assertEquals(union.byteSize(), 0); 150 assertEquals(union.byteAlignment(), 1); 151 } 152 153 @Test 154 public void testStructSizeAndAlign() { 155 MemoryLayout struct = MemoryLayout.structLayout( 156 MemoryLayout.paddingLayout(1), 157 ValueLayout.JAVA_BYTE, 158 ValueLayout.JAVA_CHAR, 159 ValueLayout.JAVA_INT, 160 ValueLayout.JAVA_LONG 161 ); 162 assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8); 163 assertEquals(struct.byteAlignment(), ADDRESS.byteSize()); 164 } 165 166 @Test(dataProvider="basicLayouts") 167 public void testPaddingNoAlign(MemoryLayout layout) { 168 assertEquals(MemoryLayout.paddingLayout(layout.byteSize()).byteAlignment(), 1); 169 } 170 171 @Test(dataProvider="basicLayouts") 172 public void testStructPaddingAndAlign(MemoryLayout layout) { 173 MemoryLayout struct = MemoryLayout.structLayout( 174 layout, MemoryLayout.paddingLayout(16 - layout.byteSize())); 175 assertEquals(struct.byteAlignment(), layout.byteAlignment()); 176 } 177 178 @Test(dataProvider="basicLayouts") 179 public void testUnionPaddingAndAlign(MemoryLayout layout) { 180 MemoryLayout struct = MemoryLayout.unionLayout( 181 layout, MemoryLayout.paddingLayout(16 - layout.byteSize())); 182 assertEquals(struct.byteAlignment(), layout.byteAlignment()); 183 } 184 185 @Test 186 public void testUnionSizeAndAlign() { 187 MemoryLayout struct = MemoryLayout.unionLayout( 188 ValueLayout.JAVA_BYTE, 189 ValueLayout.JAVA_CHAR, 190 ValueLayout.JAVA_INT, 191 ValueLayout.JAVA_LONG 192 ); 193 assertEquals(struct.byteSize(), 8); 194 assertEquals(struct.byteAlignment(), ADDRESS.byteSize()); 195 } 196 197 @Test 198 public void testSequenceBadCount() { 199 assertThrows(IllegalArgumentException.class, // negative 200 () -> MemoryLayout.sequenceLayout(-2, JAVA_SHORT)); 201 } 202 203 @Test 204 public void testSequenceNegativeElementCount() { 205 assertThrows(IllegalArgumentException.class, // negative 206 () -> MemoryLayout.sequenceLayout(-1, JAVA_SHORT)); 207 } 208 209 @Test 210 public void testSequenceOverflow() { 211 assertThrows(IllegalArgumentException.class, // negative 212 () -> MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_SHORT)); 213 assertThrows(IllegalArgumentException.class, // flip back to positive 214 () -> MemoryLayout.sequenceLayout(Long.MAX_VALUE/3, JAVA_LONG)); 215 assertThrows(IllegalArgumentException.class, // flip back to positive 216 () -> MemoryLayout.sequenceLayout(0, JAVA_LONG).withElementCount(Long.MAX_VALUE)); 217 } 218 219 @Test 220 public void testStructOverflow() { 221 assertThrows(IllegalArgumentException.class, // negative 222 () -> MemoryLayout.structLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE), 223 MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE))); 224 assertThrows(IllegalArgumentException.class, // flip back to positive 225 () -> MemoryLayout.structLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE), 226 MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE), 227 MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE))); 228 } 229 230 @Test 231 public void testPadding() { 232 var padding = MemoryLayout.paddingLayout(1); 233 assertEquals(padding.byteAlignment(), 1); 234 } 235 236 @Test 237 public void testPaddingInStruct() { 238 var padding = MemoryLayout.paddingLayout(1); 239 var struct = MemoryLayout.structLayout(padding); 240 assertEquals(struct.byteAlignment(), 1); 241 } 242 243 @Test 244 public void testPaddingIllegalByteSize() { 245 for (long byteSize : List.of(-1L, 0L)) { 246 try { 247 MemoryLayout.paddingLayout(byteSize); 248 fail("byte size cannot be " + byteSize); 249 } catch (IllegalArgumentException ignore) { 250 // Happy path 251 } 252 } 253 } 254 255 @Test 256 public void testStructToString() { 257 for (ByteOrder order : List.of(ByteOrder.LITTLE_ENDIAN, ByteOrder.BIG_ENDIAN)) { 258 String intRepresentation = (order == ByteOrder.LITTLE_ENDIAN ? "i" : "I"); 259 StructLayout padding = MemoryLayout.structLayout(JAVA_INT.withOrder(order)).withName("struct"); 260 assertEquals(padding.toString(), "[" + intRepresentation + "4](struct)"); 261 var toStringUnaligned = padding.withByteAlignment(8).toString(); 262 assertEquals(toStringUnaligned, "8%[" + intRepresentation + "4](struct)"); 263 } 264 } 265 266 @Test(dataProvider = "layoutKinds") 267 public void testPadding(LayoutKind kind) { 268 assertEquals(kind == LayoutKind.PADDING, kind.layout instanceof PaddingLayout); 269 } 270 271 @Test(dataProvider="layoutsAndAlignments") 272 public void testAlignmentString(MemoryLayout layout, long byteAlign) { 273 long[] alignments = { 1, 2, 4, 8, 16 }; 274 for (long a : alignments) { 275 if (layout.byteAlignment() == byteAlign) { 276 assertFalse(layout.toString().contains("%")); 277 if (a >= layout.byteAlignment()) { 278 assertEquals(layout.withByteAlignment(a).toString().contains("%"), a != byteAlign); 279 } 280 } 281 } 282 } 283 284 @Test(dataProvider="layoutsAndAlignments") 285 public void testBadByteAlignment(MemoryLayout layout, long byteAlign) { 286 long[] alignments = { 1, 2, 4, 8, 16 }; 287 for (long a : alignments) { 288 if (a < byteAlign && !(layout instanceof ValueLayout)) { 289 assertThrows(IllegalArgumentException.class, () -> layout.withByteAlignment(a)); 290 } 291 } 292 } 293 294 @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class) 295 public void testBadSequenceElementAlignmentTooBig(MemoryLayout layout, long byteAlign) { 296 layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align 297 MemoryLayout.sequenceLayout(1, layout); 298 } 299 300 @Test(dataProvider="layoutsAndAlignments") 301 public void testBadSequenceElementSizeNotMultipleOfAlignment(MemoryLayout layout, long byteAlign) { 302 boolean shouldFail = layout.byteSize() % layout.byteAlignment() != 0; 303 try { 304 MemoryLayout.sequenceLayout(1, layout); 305 assertFalse(shouldFail); 306 } catch (IllegalArgumentException ex) { 307 assertTrue(shouldFail); 308 } 309 } 310 311 @Test(dataProvider="layoutsAndAlignments") 312 public void testBadSpliteratorElementSizeNotMultipleOfAlignment(MemoryLayout layout, long byteAlign) { 313 boolean shouldFail = layout.byteSize() % layout.byteAlignment() != 0; 314 try (Arena arena = Arena.ofConfined()) { 315 MemorySegment segment = arena.allocate(layout); 316 segment.spliterator(layout); 317 assertFalse(shouldFail); 318 } catch (IllegalArgumentException ex) { 319 assertTrue(shouldFail); 320 } 321 } 322 323 @Test(dataProvider="layoutsAndAlignments") 324 public void testBadElementsElementSizeNotMultipleOfAlignment(MemoryLayout layout, long byteAlign) { 325 boolean shouldFail = layout.byteSize() % layout.byteAlignment() != 0; 326 try (Arena arena = Arena.ofConfined()) { 327 MemorySegment segment = arena.allocate(layout); 328 segment.elements(layout); 329 assertFalse(shouldFail); 330 } catch (IllegalArgumentException ex) { 331 assertTrue(shouldFail); 332 } 333 } 334 335 @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class) 336 public void testBadStruct(MemoryLayout layout, long byteAlign) { 337 layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align 338 MemoryLayout.structLayout(layout, layout); 339 } 340 341 @Test(expectedExceptions = IllegalArgumentException.class) 342 public void testSequenceElement() { 343 SequenceLayout layout = MemoryLayout.sequenceLayout(10, JAVA_INT); 344 // Step must be != 0 345 PathElement.sequenceElement(3, 0); 346 } 347 348 @Test 349 public void testVarHandleCaching() { 350 assertSame(JAVA_INT.varHandle(), JAVA_INT.varHandle()); 351 assertSame(JAVA_INT.withName("foo").varHandle(), JAVA_INT.varHandle()); 352 353 assertNotSame(JAVA_INT_UNALIGNED.varHandle(), JAVA_INT.varHandle()); 354 assertNotSame(ADDRESS.withTargetLayout(JAVA_INT).varHandle(), ADDRESS.varHandle()); 355 } 356 357 @Test(expectedExceptions=IllegalArgumentException.class, 358 expectedExceptionsMessageRegExp=".*Negative offset.*") 359 public void testScaleNegativeOffset() { 360 JAVA_INT.scale(-1, 0); 361 } 362 363 @Test(expectedExceptions=IllegalArgumentException.class, 364 expectedExceptionsMessageRegExp=".*Negative index.*") 365 public void testScaleNegativeIndex() { 366 JAVA_INT.scale(0, -1); 367 } 368 369 @Test(expectedExceptions=ArithmeticException.class) 370 public void testScaleAddOverflow() { 371 JAVA_INT.scale(Long.MAX_VALUE, 1); 372 } 373 374 @Test(expectedExceptions=ArithmeticException.class) 375 public void testScaleMultiplyOverflow() { 376 JAVA_INT.scale(0, Long.MAX_VALUE); 377 } 378 379 @DataProvider(name = "badAlignments") 380 public Object[][] layoutsAndBadAlignments() { 381 LayoutKind[] layoutKinds = LayoutKind.values(); 382 Object[][] values = new Object[layoutKinds.length * 2][2]; 383 for (int i = 0; i < layoutKinds.length ; i++) { 384 values[i * 2] = new Object[] { layoutKinds[i].layout, 0 }; // smaller than 1 385 values[(i * 2) + 1] = new Object[] { layoutKinds[i].layout, 5 }; // not a power of 2 386 } 387 return values; 388 } 389 390 @DataProvider(name = "layoutKinds") 391 public Object[][] layoutsKinds() { 392 return Stream.of(LayoutKind.values()) 393 .map(lk -> new Object[] { lk }) 394 .toArray(Object[][]::new); 395 } 396 397 enum SizedLayoutFactory { 398 VALUE_LE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.LITTLE_ENDIAN)), 399 VALUE_BE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.BIG_ENDIAN)), 400 PADDING(MemoryLayout::paddingLayout), 401 SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayout.paddingLayout(1))); 402 403 private final LongFunction<MemoryLayout> factory; 404 405 SizedLayoutFactory(LongFunction<MemoryLayout> factory) { 406 this.factory = factory; 407 } 408 409 MemoryLayout make(long size) { 410 return factory.apply(size); 411 } 412 } 413 414 static ValueLayout valueLayoutForSize(int size) { 415 return switch (size) { 416 case 1 -> JAVA_BYTE; 417 case 2 -> JAVA_SHORT; 418 case 4 -> JAVA_INT; 419 case 8 -> JAVA_LONG; 420 default -> throw new UnsupportedOperationException(); 421 }; 422 } 423 424 enum LayoutKind { 425 VALUE(ValueLayout.JAVA_BYTE), 426 PADDING(MemoryLayout.paddingLayout(1)), 427 SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayout.paddingLayout(1))), 428 STRUCT(MemoryLayout.structLayout(MemoryLayout.paddingLayout(1), MemoryLayout.paddingLayout(1))), 429 UNION(MemoryLayout.unionLayout(MemoryLayout.paddingLayout(1), MemoryLayout.paddingLayout(1))); 430 431 final MemoryLayout layout; 432 433 LayoutKind(MemoryLayout layout) { 434 this.layout = layout; 435 } 436 } 437 438 @DataProvider(name = "basicLayouts") 439 public Object[][] basicLayouts() { 440 return Stream.of(basicLayouts) 441 .map(l -> new Object[] { l }) 442 .toArray(Object[][]::new); 443 } 444 445 @DataProvider(name = "basicLayoutsAndAddress") 446 public Object[][] basicLayoutsAndAddress() { 447 return Stream.concat(Stream.of(basicLayouts), Stream.of(ADDRESS)) 448 .map(l -> new Object[] { l }) 449 .toArray(Object[][]::new); 450 } 451 452 @DataProvider(name = "basicLayoutsAndAddressAndGroups") 453 public Object[][] basicLayoutsAndAddressAndGroups() { 454 return Stream.concat(Stream.concat(Stream.of(basicLayouts), Stream.of(ADDRESS)), groupLayoutStream()) 455 .map(l -> new Object[] { l }) 456 .toArray(Object[][]::new); 457 } 458 459 @DataProvider(name = "layoutsAndAlignments") 460 public Object[][] layoutsAndAlignments() { 461 List<Object[]> layoutsAndAlignments = new ArrayList<>(); 462 int i = 0; 463 //add basic layouts 464 for (MemoryLayout l : basicLayoutsNoLongDouble) { 465 layoutsAndAlignments.add(new Object[] { l, l.byteAlignment() }); 466 } 467 //add basic layouts wrapped in a sequence with given size 468 for (MemoryLayout l : basicLayoutsNoLongDouble) { 469 layoutsAndAlignments.add(new Object[] { MemoryLayout.sequenceLayout(4, l), l.byteAlignment() }); 470 } 471 //add basic layouts wrapped in a struct 472 for (MemoryLayout l1 : basicLayoutsNoLongDouble) { 473 for (MemoryLayout l2 : basicLayoutsNoLongDouble) { 474 if (l1.byteSize() % l2.byteAlignment() != 0) continue; // second element is not aligned, skip 475 long align = Math.max(l1.byteAlignment(), l2.byteAlignment()); 476 layoutsAndAlignments.add(new Object[]{MemoryLayout.structLayout(l1, l2), align}); 477 } 478 } 479 //add basic layouts wrapped in a union 480 for (MemoryLayout l1 : basicLayoutsNoLongDouble) { 481 for (MemoryLayout l2 : basicLayoutsNoLongDouble) { 482 long align = Math.max(l1.byteAlignment(), l2.byteAlignment()); 483 layoutsAndAlignments.add(new Object[]{MemoryLayout.unionLayout(l1, l2), align}); 484 } 485 } 486 return layoutsAndAlignments.toArray(Object[][]::new); 487 } 488 489 @DataProvider(name = "groupLayouts") 490 public Object[][] groupLayouts() { 491 return groupLayoutStream() 492 .map(l -> new Object[] { l }) 493 .toArray(Object[][]::new); 494 } 495 496 @DataProvider(name = "validCarriers") 497 public Object[][] validCarriers() { 498 return Stream.of( 499 boolean.class, 500 byte.class, 501 char.class, 502 short.class, 503 int.class, 504 long.class, 505 float.class, 506 double.class, 507 MemorySegment.class 508 ) 509 .map(l -> new Object[]{l}) 510 .toArray(Object[][]::new); 511 } 512 513 static Stream<MemoryLayout> groupLayoutStream() { 514 return Stream.of( 515 MemoryLayout.sequenceLayout(10, JAVA_INT), 516 MemoryLayout.structLayout(JAVA_INT, MemoryLayout.paddingLayout(4), JAVA_LONG), 517 MemoryLayout.unionLayout(JAVA_LONG, JAVA_DOUBLE) 518 ); 519 } 520 521 static ValueLayout[] basicLayouts = { 522 ValueLayout.JAVA_BYTE, 523 ValueLayout.JAVA_CHAR, 524 ValueLayout.JAVA_SHORT, 525 ValueLayout.JAVA_INT, 526 ValueLayout.JAVA_FLOAT, 527 ValueLayout.JAVA_LONG, 528 ValueLayout.JAVA_DOUBLE, 529 }; 530 531 static MemoryLayout[] basicLayoutsNoLongDouble = Stream.of(basicLayouts) 532 .filter(l -> l.carrier() != long.class && l.carrier() != double.class) 533 .toArray(MemoryLayout[]::new); 534 }