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 import java.lang.foreign.Arena;
 26 import java.lang.foreign.MemoryLayout;
 27 import java.lang.foreign.MemorySegment;
 28 import java.lang.foreign.ValueLayout;
 29 import java.lang.invoke.VarHandle;
 30 import java.util.ArrayList;
 31 import java.util.List;
 32 
 33 import org.testng.annotations.*;
 34 import static org.testng.Assert.*;
 35 
 36 /*
 37  * @test
 38  * @run testng/othervm -Xverify:all TestSlices
 39  */
 40 public class TestSlices {
 41 
 42     static MemoryLayout LAYOUT = MemoryLayout.sequenceLayout(2,
 43             MemoryLayout.sequenceLayout(5, ValueLayout.JAVA_INT));
 44 
 45     static VarHandle VH_ALL = LAYOUT.varHandle(
 46             MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());
 47 
 48     @Test(dataProvider = "slices")
 49     public void testSlices(VarHandle handle, int lo, int hi, int[] values) {
 50         try (Arena arena = Arena.ofConfined()) {
 51             MemorySegment segment = arena.allocate(LAYOUT);;
 52             //init
 53             for (long i = 0 ; i < 2 ; i++) {
 54                 for (long j = 0 ; j < 5 ; j++) {
 55                     VH_ALL.set(segment, 0L, i, j, (int)j + 1 + ((int)i * 5));
 56                 }
 57             }
 58 
 59             checkSlice(segment, handle, lo, hi, values);
 60         }
 61     }
 62 
 63     @Test(dataProvider = "slices")
 64     public void testSliceBadIndex(VarHandle handle, int lo, int hi, int[] values) {
 65         try (Arena arena = Arena.ofConfined()) {
 66             MemorySegment segment = arena.allocate(LAYOUT);;
 67             assertThrows(() -> handle.get(segment, 0L, lo, 0));
 68             assertThrows(() -> handle.get(segment, 0L, 0, hi));
 69         }
 70     }
 71 
 72     static void checkSlice(MemorySegment segment, VarHandle handle, long i_max, long j_max, int... values) {
 73         int index = 0;
 74         for (long i = 0 ; i < i_max ; i++) {
 75             for (long j = 0 ; j < j_max ; j++) {
 76                 int x = (int) handle.get(segment, 0L, i, j);
 77                 assertEquals(x, values[index++]);
 78             }
 79         }
 80         assertEquals(index, values.length);
 81     }
 82 
 83     @Test(expectedExceptions = IndexOutOfBoundsException.class)
 84     public void testSliceNegativeOffset() {
 85         MemorySegment.ofArray(new byte[100]).asSlice(-1);
 86     }
 87 
 88     @Test(expectedExceptions = IndexOutOfBoundsException.class)
 89     public void testSliceNegativeOffsetGoodSize() {
 90         MemorySegment.ofArray(new byte[100]).asSlice(-1, 10);
 91     }
 92 
 93     @Test(expectedExceptions = IndexOutOfBoundsException.class)
 94     public void testSliceGoodOffsetNegativeSize() {
 95         MemorySegment.ofArray(new byte[100]).asSlice(10, -1);
 96     }
 97 
 98     @Test(expectedExceptions = IndexOutOfBoundsException.class)
 99     public void testSliceNegativeOffsetGoodLayout() {
100         MemorySegment.ofArray(new byte[100]).asSlice(-1, ValueLayout.JAVA_INT);
101     }
102 
103     @Test(expectedExceptions = IndexOutOfBoundsException.class)
104     public void testSliceOffsetTooBig() {
105         MemorySegment.ofArray(new byte[100]).asSlice(120);
106     }
107 
108     @Test(expectedExceptions = IndexOutOfBoundsException.class)
109     public void testSliceOffsetTooBigSizeGood() {
110         MemorySegment.ofArray(new byte[100]).asSlice(120, 0);
111     }
112 
113     @Test(expectedExceptions = IndexOutOfBoundsException.class)
114     public void testSliceOffsetOkSizeTooBig() {
115         MemorySegment.ofArray(new byte[100]).asSlice(0, 120);
116     }
117 
118     @Test(expectedExceptions = IndexOutOfBoundsException.class)
119     public void testSliceLayoutTooBig() {
120         MemorySegment.ofArray(new byte[100])
121                 .asSlice(0, MemoryLayout.sequenceLayout(120, ValueLayout.JAVA_BYTE));
122     }
123 
124     @Test(dataProvider = "segmentsAndLayouts")
125     public void testSliceAlignment(MemorySegment segment, long alignment, ValueLayout layout) {
126         boolean badAlign = layout.byteAlignment() > alignment;
127         try {
128             segment.asSlice(0, layout);
129             assertFalse(badAlign);
130         } catch (IllegalArgumentException ex) {
131             assertTrue(badAlign);
132             assertTrue(ex.getMessage().contains("incompatible with alignment constraints"));
133         }
134     }
135 
136     @Test
137     public void testSliceAlignmentPowerOfTwo() {
138         try (Arena arena = Arena.ofConfined()) {
139             MemorySegment segment = arena.allocate(100, 4096);
140             for (int i = 8 ; i < 4096 ; i++) {
141                 boolean badAlign = Long.bitCount(i) != 1; // not a power of two
142                 try {
143                     segment.asSlice(0, 100, i);
144                     assertFalse(badAlign);
145                 } catch (IllegalArgumentException iae) {
146                     assertTrue(badAlign);
147                 }
148             }
149         }
150     }
151 
152     @DataProvider(name = "slices")
153     static Object[][] slices() {
154         return new Object[][] {
155                 // x
156                 { VH_ALL, 2, 5, new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } },
157                 // x[0::2]
158                 { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(),
159                         MemoryLayout.PathElement.sequenceElement(0, 2)), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } },
160                 // x[1::2]
161                 { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(),
162                         MemoryLayout.PathElement.sequenceElement(1, 2)), 2, 2, new int[] { 2, 4, 7, 9 } },
163                 // x[4::-2]
164                 { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(),
165                         MemoryLayout.PathElement.sequenceElement(4, -2)), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } },
166                 // x[3::-2]
167                 { LAYOUT.varHandle(MemoryLayout.PathElement.sequenceElement(),
168                         MemoryLayout.PathElement.sequenceElement(3, -2)), 2, 2, new int[] { 4, 2, 9, 7 } },
169         };
170     }
171 
172     @DataProvider(name = "segmentsAndLayouts")
173     static Object[][] segmentsAndLayouts() {
174         List<Object[]> segmentsAndLayouts = new ArrayList<>();
175         for (SegmentKind sk : SegmentKind.values()) {
176             for (LayoutKind lk : LayoutKind.values()) {
177                 for (int align : new int[]{ 1, 2, 4, 8 }) {
178                     if (align > sk.maxAlign) break;
179                     segmentsAndLayouts.add(new Object[] { sk.segment.asSlice(align), align, lk.layout });
180                 }
181             }
182         }
183         return segmentsAndLayouts.toArray(Object[][]::new);
184     }
185 
186     enum SegmentKind {
187         NATIVE(Arena.ofAuto().allocate(100), 8),
188         BYTE_ARRAY(MemorySegment.ofArray(new byte[100]), 1),
189         CHAR_ARRAY(MemorySegment.ofArray(new char[100]), 2),
190         SHORT_ARRAY(MemorySegment.ofArray(new short[100]), 2),
191         INT_ARRAY(MemorySegment.ofArray(new int[100]), 4),
192         FLOAT_ARRAY(MemorySegment.ofArray(new float[100]), 4),
193         LONG_ARRAY(MemorySegment.ofArray(new long[100]), 8),
194         DOUBLE_ARRAY(MemorySegment.ofArray(new double[100]), 8);
195 
196 
197         final MemorySegment segment;
198         final int maxAlign;
199 
200         SegmentKind(MemorySegment segment, int maxAlign) {
201             this.segment = segment;
202             this.maxAlign = maxAlign;
203         }
204     }
205 
206     enum LayoutKind {
207         BOOL(ValueLayout.JAVA_BOOLEAN),
208         CHAR(ValueLayout.JAVA_CHAR),
209         SHORT(ValueLayout.JAVA_SHORT),
210         INT(ValueLayout.JAVA_INT),
211         FLOAT(ValueLayout.JAVA_FLOAT),
212         LONG(ValueLayout.JAVA_LONG),
213         DOUBLE(ValueLayout.JAVA_DOUBLE),
214         ADDRESS(ValueLayout.ADDRESS);
215 
216 
217         final ValueLayout layout;
218 
219         LayoutKind(ValueLayout segment) {
220             this.layout = segment;
221         }
222     }
223 }