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