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 import static org.testng.Assert.*;
 38 
 39 public class TestLayouts {
 40 
 41     @Test(dataProvider = "badLayoutSizes", expectedExceptions = IllegalArgumentException.class)
 42     public void testBadLayoutSize(SizedLayoutFactory factory, long size) {
 43         factory.make(size);
 44     }
 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                 MemoryLayouts.JAVA_INT.withName("size"),
 55                 MemoryLayout.paddingLayout(32),
 56                 MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_DOUBLE).withName("arr"));
 57         assertFalse(layout.hasSize());
 58         VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
 59         VarHandle array_elem_handle = layout.varHandle(double.class,
 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                 MemoryLayouts.JAVA_INT.withName("size"),
 81                 MemoryLayout.paddingLayout(32),
 82                 MemoryLayout.sequenceLayout(1, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_DOUBLE)).withName("arr"));
 83         assertFalse(layout.hasSize());
 84         VarHandle size_handle = layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("size"));
 85         VarHandle array_elem_handle = layout.varHandle(double.class,
 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, MemoryLayouts.JAVA_INT);
107         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
108             MemorySegment segment = MemorySegment.allocateNative(seq, scope);
109             VarHandle indexHandle = seq.varHandle(int.class, 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(int.class, 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(MemoryLayouts.JAVA_INT);
147         seq.withElementCount(-1);
148     }
149 
150     @Test(expectedExceptions = IllegalArgumentException.class)
151     public void testBadBoundSequenceLayoutResize() {
152         SequenceLayout seq = MemoryLayout.sequenceLayout(10, MemoryLayouts.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                 MemoryLayouts.JAVA_BYTE,
172                 MemoryLayouts.JAVA_CHAR,
173                 MemoryLayouts.JAVA_INT,
174                 MemoryLayouts.JAVA_LONG
175         );
176         assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8);
177         assertEquals(struct.byteAlignment(), MemoryLayouts.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                 MemoryLayouts.JAVA_BYTE,
203                 MemoryLayouts.JAVA_CHAR,
204                 MemoryLayouts.JAVA_INT,
205                 MemoryLayouts.JAVA_LONG
206         );
207         assertEquals(struct.byteSize(), 8);
208         assertEquals(struct.byteAlignment(), MemoryLayouts.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 = "badLayoutSizes")
228     public Object[][] factoriesAndSizes() {
229         return new Object[][] {
230                 { SizedLayoutFactory.VALUE_BE, 0 },
231                 { SizedLayoutFactory.VALUE_BE, -1 },
232                 { SizedLayoutFactory.VALUE_LE, 0 },
233                 { SizedLayoutFactory.VALUE_LE, -1 },
234                 { SizedLayoutFactory.PADDING, 0 },
235                 { SizedLayoutFactory.PADDING, -1 },
236                 { SizedLayoutFactory.SEQUENCE, -1 }
237         };
238     }
239 
240     @DataProvider(name = "unboundLayouts")
241     public Object[][] unboundLayouts() {
242         return new Object[][] {
243                 { MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT), 32 },
244                 { MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
245                 { MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
246                 { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
247                 { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
248                 { MemoryLayout.structLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
249                 { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT)), 32 },
250                 { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
251                 { MemoryLayout.unionLayout(MemoryLayout.sequenceLayout(4, MemoryLayout.sequenceLayout(MemoryLayouts.JAVA_INT))), 32 },
252         };
253     }
254 
255     @DataProvider(name = "badAlignments")
256     public Object[][] layoutsAndBadAlignments() {
257         LayoutKind[] layoutKinds = LayoutKind.values();
258         Object[][] values = new Object[layoutKinds.length * 2][2];
259         for (int i = 0; i < layoutKinds.length ; i++) {
260             values[i * 2] = new Object[] { layoutKinds[i].layout, 3 }; // smaller than 8
261             values[(i * 2) + 1] = new Object[] { layoutKinds[i].layout, 18 }; // not a power of 2
262         }
263         return values;
264     }
265 
266     @DataProvider(name = "layoutKinds")
267     public Object[][] layoutsKinds() {
268         return Stream.of(LayoutKind.values())
269                 .map(lk -> new Object[] { lk })
270                 .toArray(Object[][]::new);
271     }
272 
273     enum SizedLayoutFactory {
274         VALUE_LE(size -> MemoryLayout.valueLayout(size, ByteOrder.LITTLE_ENDIAN)),
275         VALUE_BE(size -> MemoryLayout.valueLayout(size, ByteOrder.BIG_ENDIAN)),
276         PADDING(MemoryLayout::paddingLayout),
277         SEQUENCE(size -> MemoryLayout.sequenceLayout(size, MemoryLayouts.PAD_8));
278 
279         private final LongFunction<MemoryLayout> factory;
280 
281         SizedLayoutFactory(LongFunction<MemoryLayout> factory) {
282             this.factory = factory;
283         }
284 
285         MemoryLayout make(long size) {
286             return factory.apply(size);
287         }
288     }
289 
290     enum LayoutKind {
291         VALUE_LE(MemoryLayouts.BITS_8_LE),
292         VALUE_BE(MemoryLayouts.BITS_8_BE),
293         PADDING(MemoryLayouts.PAD_8),
294         SEQUENCE(MemoryLayout.sequenceLayout(1, MemoryLayouts.PAD_8)),
295         STRUCT(MemoryLayout.structLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8)),
296         UNION(MemoryLayout.unionLayout(MemoryLayouts.PAD_8, MemoryLayouts.PAD_8));
297 
298         final MemoryLayout layout;
299 
300         LayoutKind(MemoryLayout layout) {
301             this.layout = layout;
302         }
303     }
304 
305     @DataProvider(name = "basicLayouts")
306     public Object[][] basicLayouts() {
307         return Stream.of(basicLayouts)
308                 .map(l -> new Object[] { l })
309                 .toArray(Object[][]::new);
310     }
311 
312     @DataProvider(name = "layoutsAndAlignments")
313     public Object[][] layoutsAndAlignments() {
314         Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 4][];
315         int i = 0;
316         //add basic layouts
317         for (MemoryLayout l : basicLayouts) {
318             layoutsAndAlignments[i++] = new Object[] { l, l.bitAlignment() };
319         }
320         //add basic layouts wrapped in a sequence with given size
321         for (MemoryLayout l : basicLayouts) {
322             layoutsAndAlignments[i++] = new Object[] { MemoryLayout.sequenceLayout(4, l), l.bitAlignment() };
323         }
324         //add basic layouts wrapped in a struct
325         for (MemoryLayout l : basicLayouts) {
326             layoutsAndAlignments[i++] = new Object[] { MemoryLayout.structLayout(l), l.bitAlignment() };
327         }
328         //add basic layouts wrapped in a union
329         for (MemoryLayout l : basicLayouts) {
330             layoutsAndAlignments[i++] = new Object[] { MemoryLayout.unionLayout(l), l.bitAlignment() };
331         }
332         return layoutsAndAlignments;
333     }
334 
335     static MemoryLayout[] basicLayouts = {
336             MemoryLayouts.JAVA_BYTE,
337             MemoryLayouts.JAVA_CHAR,
338             MemoryLayouts.JAVA_SHORT,
339             MemoryLayouts.JAVA_INT,
340             MemoryLayouts.JAVA_FLOAT,
341             MemoryLayouts.JAVA_LONG,
342             MemoryLayouts.JAVA_DOUBLE,
343     };
344 }