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