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