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