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 TestMemoryAlignment
 27  */
 28 
 29 import java.lang.foreign.*;
 30 import java.lang.foreign.MemoryLayout.PathElement;
 31 import java.lang.invoke.VarHandle;
 32 import java.nio.ByteOrder;
 33 import java.util.stream.LongStream;
 34 
 35 import org.testng.annotations.*;
 36 import static org.testng.Assert.*;
 37 
 38 public class TestMemoryAlignment {
 39 
 40     @Test(dataProvider = "alignments")
 41     public void testAlignedAccess(long align) {
 42         ValueLayout layout = ValueLayout.JAVA_INT
 43                 .withOrder(ByteOrder.BIG_ENDIAN);
 44         assertEquals(layout.byteAlignment(), 4);
 45         ValueLayout aligned = layout.withByteAlignment(align);
 46         assertEquals(aligned.byteAlignment(), align); //unreasonable alignment here, to make sure access throws
 47         VarHandle vh = aligned.varHandle();
 48         try (Arena arena = Arena.ofConfined()) {
 49             MemorySegment segment = arena.allocate(aligned);;
 50             vh.set(segment, 0L, -42);
 51             int val = (int)vh.get(segment, 0L);
 52             assertEquals(val, -42);
 53         }
 54     }
 55 
 56     @Test(dataProvider = "alignments")
 57     public void testUnalignedAccess(long align) {
 58         ValueLayout layout = ValueLayout.JAVA_INT
 59                 .withOrder(ByteOrder.BIG_ENDIAN);
 60         assertEquals(layout.byteAlignment(), 4);
 61         ValueLayout aligned = layout.withByteAlignment(align);
 62         try (Arena arena = Arena.ofConfined()) {
 63             MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(1), aligned);
 64             assertEquals(alignedGroup.byteAlignment(), align);
 65             VarHandle vh = aligned.varHandle();
 66             MemorySegment segment = arena.allocate(alignedGroup);;
 67             vh.set(segment.asSlice(1L), 0L, -42);
 68             assertEquals(align, 8); //this is the only case where access is aligned
 69         } catch (IllegalArgumentException ex) {
 70             assertNotEquals(align, 8); //if align != 8, access is always unaligned
 71         }
 72     }
 73 
 74     @Test(dataProvider = "alignments")
 75     public void testUnalignedPath(long align) {
 76         MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
 77         MemoryLayout aligned = layout.withByteAlignment(align).withName("value");
 78         try {
 79             GroupLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(1), aligned);
 80             alignedGroup.varHandle(PathElement.groupElement("value"));
 81             assertEquals(align, 1); //this is the only case where path is aligned
 82         } catch (IllegalArgumentException ex) {
 83             assertNotEquals(align, 1); //if align != 8, path is always unaligned
 84         }
 85     }
 86 
 87     @Test(dataProvider = "alignments")
 88     public void testUnalignedSequence(long align) {
 89         try {
 90             SequenceLayout layout = MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN).withByteAlignment(align));
 91             VarHandle vh = layout.varHandle(PathElement.sequenceElement());
 92             try (Arena arena = Arena.ofConfined()) {
 93                 MemorySegment segment = arena.allocate(layout);;
 94                 for (long i = 0 ; i < 5 ; i++) {
 95                     vh.set(segment, 0L, i, -42);
 96                 }
 97             }
 98         } catch (IllegalArgumentException ex) {
 99             assertTrue(align > 4); //if align > 4, access is always unaligned (for some elements)
100         }
101     }
102 
103     @Test
104     public void testPackedAccess() {
105         ValueLayout vChar = ValueLayout.JAVA_BYTE;
106         ValueLayout vShort = ValueLayout.JAVA_SHORT.withOrder(ByteOrder.BIG_ENDIAN);
107         ValueLayout vInt = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
108         //mimic pragma pack(1)
109         GroupLayout g = MemoryLayout.structLayout(vChar.withByteAlignment(1).withName("a"),
110                                vShort.withByteAlignment(1).withName("b"),
111                                vInt.withByteAlignment(1).withName("c"));
112         assertEquals(g.byteAlignment(), 1);
113         VarHandle vh_c = g.varHandle(PathElement.groupElement("a"));
114         VarHandle vh_s = g.varHandle(PathElement.groupElement("b"));
115         VarHandle vh_i = g.varHandle(PathElement.groupElement("c"));
116         try (Arena arena = Arena.ofConfined()) {
117             MemorySegment segment = arena.allocate(g);;
118             vh_c.set(segment, 0L, Byte.MIN_VALUE);
119             assertEquals(vh_c.get(segment, 0L), Byte.MIN_VALUE);
120             vh_s.set(segment, 0L, Short.MIN_VALUE);
121             assertEquals(vh_s.get(segment, 0L), Short.MIN_VALUE);
122             vh_i.set(segment, 0L, Integer.MIN_VALUE);
123             assertEquals(vh_i.get(segment, 0L), Integer.MIN_VALUE);
124         }
125     }
126 
127     @DataProvider(name = "alignments")
128     public Object[][] createAlignments() {
129         return LongStream.range(1, 20)
130                 .mapToObj(v -> new Object[] { 1L << v })
131                 .toArray(Object[][]::new);
132     }
133 }