1 /*
  2  *  Copyright (c) 2019, 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 jdk.incubator.foreign.*;
 30 
 31 import java.lang.invoke.VarHandle;
 32 import java.nio.ByteOrder;
 33 import java.util.function.LongFunction;
 34 import java.util.stream.Stream;
 35 
 36 import org.testng.annotations.*;
 37 
 38 import static jdk.incubator.foreign.ValueLayout.JAVA_BYTE;
 39 import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
 40 import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
 41 import static jdk.incubator.foreign.ValueLayout.JAVA_SHORT;
 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.withBitAlignment(alignment);
 49     }
 50 
 51     @Test
 52     public void testVLAInStruct() {
 53         MemoryLayout layout = MemoryLayout.structLayout(
 54                 ValueLayout.JAVA_INT.withName("size"),
 55                 MemoryLayout.paddingLayout(32),
 56                 MemoryLayout.sequenceLayout(ValueLayout.JAVA_DOUBLE).withName("arr"));
 57         assertFalse(layout.hasSize());
 58         VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size"));
 59         VarHandle array_elem_handle = layout.varHandle(
 60                 MemoryLayout.PathElement.groupElement("arr"),
 61                 MemoryLayout.PathElement.sequenceElement());
 62         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
 63             MemorySegment segment = MemorySegment.allocateNative(
 64                     layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr")), scope);
 65             size_handle.set(segment, 4);
 66             for (int i = 0 ; i < 4 ; i++) {
 67                 array_elem_handle.set(segment, i, (double)i);
 68             }
 69             //check
 70             assertEquals(4, (int)size_handle.get(segment));
 71             for (int i = 0 ; i < 4 ; i++) {
 72                 assertEquals((double)i, (double)array_elem_handle.get(segment, i));
 73             }
 74         }
 75     }
 76 
 77     @Test
 78     public void testVLAInSequence() {
 79         MemoryLayout layout = MemoryLayout.structLayout(
 80                 ValueLayout.JAVA_INT.withName("size"),
 81                 MemoryLayout.paddingLayout(32),
 82                 MemoryLayout.sequenceLayout(1, MemoryLayout.sequenceLayout(ValueLayout.JAVA_DOUBLE)).withName("arr"));
 83         assertFalse(layout.hasSize());
 84         VarHandle size_handle = layout.varHandle(MemoryLayout.PathElement.groupElement("size"));
 85         VarHandle array_elem_handle = layout.varHandle(
 86                 MemoryLayout.PathElement.groupElement("arr"),
 87                 MemoryLayout.PathElement.sequenceElement(0),
 88                 MemoryLayout.PathElement.sequenceElement());
 89         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
 90             MemorySegment segment = MemorySegment.allocateNative(
 91                     layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.sequenceElement()), scope);
 92             size_handle.set(segment, 4);
 93             for (int i = 0 ; i < 4 ; i++) {
 94                 array_elem_handle.set(segment, i, (double)i);
 95             }
 96             //check
 97             assertEquals(4, (int)size_handle.get(segment));
 98             for (int i = 0 ; i < 4 ; i++) {
 99                 assertEquals((double)i, (double)array_elem_handle.get(segment, i));
100             }
101         }
102     }
103 
104     @Test
105     public void testIndexedSequencePath() {
106         MemoryLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
107         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
108             MemorySegment segment = MemorySegment.allocateNative(seq, scope);
109             VarHandle indexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement());
110             // init segment
111             for (int i = 0 ; i < 10 ; i++) {
112                 indexHandle.set(segment, (long)i, i);
113             }
114             //check statically indexed handles
115             for (int i = 0 ; i < 10 ; i++) {
116                 VarHandle preindexHandle = seq.varHandle(MemoryLayout.PathElement.sequenceElement(i));
117                 int expected = (int)indexHandle.get(segment, (long)i);
118                 int found = (int)preindexHandle.get(segment);
119                 assertEquals(expected, found);
120             }
121         }
122     }
123 
124     @Test(dataProvider = "unboundLayouts", expectedExceptions = UnsupportedOperationException.class)
125     public void testUnboundSize(MemoryLayout layout, long align) {
126         layout.bitSize();
127     }
128 
129     @Test(dataProvider = "unboundLayouts")
130     public void testUnboundAlignment(MemoryLayout layout, long align) {
131         assertEquals(align, layout.bitAlignment());
132     }
133 
134     @Test(dataProvider = "unboundLayouts")
135     public void testUnboundEquals(MemoryLayout layout, long align) {
136         assertTrue(layout.equals(layout));
137     }
138 
139     @Test(dataProvider = "unboundLayouts")
140     public void testUnboundHash(MemoryLayout layout, long align) {
141         layout.hashCode();
142     }
143 
144     @Test(expectedExceptions = IllegalArgumentException.class)
145     public void testBadUnboundSequenceLayoutResize() {
146         SequenceLayout seq = MemoryLayout.sequenceLayout(ValueLayout.JAVA_INT);
147         seq.withElementCount(-1);
148     }
149 
150     @Test(expectedExceptions = IllegalArgumentException.class)
151     public void testBadBoundSequenceLayoutResize() {
152         SequenceLayout seq = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
153         seq.withElementCount(-1);
154     }
155 
156     @Test
157     public void testEmptyGroup() {
158         MemoryLayout struct = MemoryLayout.structLayout();
159         assertEquals(struct.bitSize(), 0);
160         assertEquals(struct.bitAlignment(), 1);
161 
162         MemoryLayout union = MemoryLayout.unionLayout();
163         assertEquals(union.bitSize(), 0);
164         assertEquals(union.bitAlignment(), 1);
165     }
166 
167     @Test
168     public void testStructSizeAndAlign() {
169         MemoryLayout struct = MemoryLayout.structLayout(
170                 MemoryLayout.paddingLayout(8),
171                 ValueLayout.JAVA_BYTE,
172                 ValueLayout.JAVA_CHAR,
173                 ValueLayout.JAVA_INT,
174                 ValueLayout.JAVA_LONG
175         );
176         assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8);
177         assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment());
178     }
179 
180     @Test(dataProvider="basicLayouts")
181     public void testPaddingNoAlign(MemoryLayout layout) {
182         assertEquals(MemoryLayout.paddingLayout(layout.bitSize()).bitAlignment(), 1);
183     }
184 
185     @Test(dataProvider="basicLayouts")
186     public void testStructPaddingAndAlign(MemoryLayout layout) {
187         MemoryLayout struct = MemoryLayout.structLayout(
188                 layout, MemoryLayout.paddingLayout(128 - layout.bitSize()));
189         assertEquals(struct.bitAlignment(), layout.bitAlignment());
190     }
191 
192     @Test(dataProvider="basicLayouts")
193     public void testUnionPaddingAndAlign(MemoryLayout layout) {
194         MemoryLayout struct = MemoryLayout.unionLayout(
195                 layout, MemoryLayout.paddingLayout(128 - layout.bitSize()));
196         assertEquals(struct.bitAlignment(), layout.bitAlignment());
197     }
198 
199     @Test
200     public void testUnionSizeAndAlign() {
201         MemoryLayout struct = MemoryLayout.unionLayout(
202                 ValueLayout.JAVA_BYTE,
203                 ValueLayout.JAVA_CHAR,
204                 ValueLayout.JAVA_INT,
205                 ValueLayout.JAVA_LONG
206         );
207         assertEquals(struct.byteSize(), 8);
208         assertEquals(struct.byteAlignment(), ValueLayout.ADDRESS.byteAlignment());
209     }
210 
211     @Test(dataProvider = "layoutKinds")
212     public void testPadding(LayoutKind kind) {
213         assertEquals(kind == LayoutKind.PADDING, kind.layout.isPadding());
214     }
215 
216     @Test(dataProvider="layoutsAndAlignments")
217     public void testAlignmentString(MemoryLayout layout, long bitAlign) {
218         long[] alignments = { 8, 16, 32, 64, 128 };
219         for (long a : alignments) {
220             if (layout.bitAlignment() == layout.bitSize()) {
221                 assertFalse(layout.toString().contains("%"));
222                 assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign);
223             }
224         }
225     }
226 
227     @DataProvider(name = "unboundLayouts")
228     public Object[][] unboundLayouts() {
229         ValueLayout alignedInt = JAVA_INT.withBitAlignment(32);
230         return new Object[][] {
231                 { MemoryLayout.sequenceLayout(alignedInt), 32 },
232                 { MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
233                 { MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt)), 32 },
234                 { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
235                 { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 },
236                 { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 },
237                 { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(alignedInt)), 32 },
238                 { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(alignedInt))), 32 },
239                 { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(alignedInt))), 32 },
240         };
241     }
242 
243     @DataProvider(name = "badAlignments")
244     public Object[][] layoutsAndBadAlignments() {
245         LayoutKind[] layoutKinds = LayoutKind.values();
246         Object[][] values = new Object[layoutKinds.length * 2][2];
247         for (int i = 0; i < layoutKinds.length ; i++) {
248             values[i * 2] = new Object[] { layoutKinds[i].layout, 3 }; // smaller than 8
249             values[(i * 2) + 1] = new Object[] { layoutKinds[i].layout, 18 }; // not a power of 2
250         }
251         return values;
252     }
253 
254     @DataProvider(name = "layoutKinds")
255     public Object[][] layoutsKinds() {
256         return Stream.of(LayoutKind.values())
257                 .map(lk -> new Object[] { lk })
258                 .toArray(Object[][]::new);
259     }
260 
261     enum SizedLayoutFactory {
262         VALUE_LE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.LITTLE_ENDIAN)),
263         VALUE_BE(size -> valueLayoutForSize((int)size).withOrder(ByteOrder.BIG_ENDIAN)),
264         PADDING(MemoryLayout::paddingLayout),
265         SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayout.paddingLayout(8)));
266 
267         private final LongFunction<MemoryLayout> factory;
268 
269         SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
270             this.factory = factory;
271         }
272 
273         MemoryLayout make(long size) {
274             return factory.apply(size);
275         }
276     }
277 
278     static ValueLayout valueLayoutForSize(int size) {
279         return switch (size) {
280             case 1 -> JAVA_BYTE;
281             case 2 -> JAVA_SHORT;
282             case 4 -> JAVA_INT;
283             case 8 -> JAVA_LONG;
284             default -> throw new UnsupportedOperationException();
285         };
286     }
287 
288     enum LayoutKind {
289         VALUE(ValueLayout.JAVA_BYTE),
290         PADDING(MemoryLayout.paddingLayout(8)),
291         SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayout.paddingLayout(8))),
292         STRUCT(MemoryLayout.structLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8))),
293         UNION(MemoryLayout.unionLayout(MemoryLayout.paddingLayout(8), MemoryLayout.paddingLayout(8)));
294 
295         final MemoryLayout layout;
296 
297         LayoutKind(MemoryLayout layout) {
298             this.layout = layout;
299         }
300     }
301 
302     @DataProvider(name = "basicLayouts")
303     public Object[][] basicLayouts() {
304         return Stream.of(basicLayouts)
305                 .map(l -> new Object[] { l })
306                 .toArray(Object[][]::new);
307     }
308 
309     @DataProvider(name = "layoutsAndAlignments")
310     public Object[][] layoutsAndAlignments() {
311         Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 4][];
312         int i = 0;
313         //add basic layouts
314         for (MemoryLayout l : basicLayouts) {
315             layoutsAndAlignments[i++] = new Object[] { l, l.bitAlignment() };
316         }
317         //add basic layouts wrapped in a sequence with given size
318         for (MemoryLayout l : basicLayouts) {
319             layoutsAndAlignments[i++] = new Object[] { MemoryLayout.sequenceLayout(4, l), l.bitAlignment() };
320         }
321         //add basic layouts wrapped in a struct
322         for (MemoryLayout l : basicLayouts) {
323             layoutsAndAlignments[i++] = new Object[] { MemoryLayout.structLayout(l), l.bitAlignment() };
324         }
325         //add basic layouts wrapped in a union
326         for (MemoryLayout l : basicLayouts) {
327             layoutsAndAlignments[i++] = new Object[] { MemoryLayout.unionLayout(l), l.bitAlignment() };
328         }
329         return layoutsAndAlignments;
330     }
331 
332     static MemoryLayout[] basicLayouts = {
333             ValueLayout.JAVA_BYTE,
334             ValueLayout.JAVA_CHAR,
335             ValueLayout.JAVA_SHORT,
336             ValueLayout.JAVA_INT,
337             ValueLayout.JAVA_FLOAT,
338             ValueLayout.JAVA_LONG,
339             ValueLayout.JAVA_DOUBLE,
340     };
341 }