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 /* 26 * @test 27 * @enablePreview 28 * @run testng TestSegmentCopy 29 */ 30 31 import java.lang.foreign.Arena; 32 import java.lang.foreign.MemorySegment; 33 import java.lang.foreign.ValueLayout; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.invoke.VarHandle; 36 import java.nio.ByteOrder; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.List; 40 import java.util.function.IntFunction; 41 42 import org.testng.SkipException; 43 import org.testng.annotations.DataProvider; 44 import org.testng.annotations.Test; 45 46 import static java.lang.foreign.ValueLayout.JAVA_BYTE; 47 import static org.testng.Assert.*; 48 49 public class TestSegmentCopy { 50 51 static final int TEST_BYTE_SIZE = 16; 52 53 @Test(dataProvider = "segmentKinds") 54 public void testByteCopy(SegmentKind kind1, SegmentKind kind2) { 55 MemorySegment s1 = kind1.makeSegment(TEST_BYTE_SIZE); 56 MemorySegment s2 = kind2.makeSegment(TEST_BYTE_SIZE); 57 58 // for all offsets 59 for (int s1Offset = 0; s1Offset < s1.byteSize(); s1Offset++) { 60 for (int s2Offset = 0; s2Offset < s2.byteSize(); s2Offset++) { 61 long slice1ByteSize = s1.byteSize() - s1Offset; 62 long slice2ByteSize = s2.byteSize() - s2Offset; 63 64 long copySize = Math.min(slice1ByteSize, slice2ByteSize); 65 66 //prepare source slice 67 for (int i = 0 ; i < copySize; i++) { 68 Type.BYTE.set(s1, s1Offset, i, i); 69 } 70 //perform copy 71 MemorySegment.copy(s1, Type.BYTE.layout, s1Offset, s2, Type.BYTE.layout, s2Offset, copySize); 72 //check that copy actually worked 73 for (int i = 0; i < copySize; i++) { 74 Type.BYTE.check(s2, s2Offset, i, i); 75 } 76 } 77 } 78 } 79 80 @Test(expectedExceptions = UnsupportedOperationException.class, dataProvider = "segmentKinds") 81 public void testReadOnlyCopy(SegmentKind kind1, SegmentKind kind2) { 82 MemorySegment s1 = kind1.makeSegment(TEST_BYTE_SIZE); 83 MemorySegment s2 = kind2.makeSegment(TEST_BYTE_SIZE); 84 // check failure with read-only dest 85 MemorySegment.copy(s1, Type.BYTE.layout, 0, s2.asReadOnly(), Type.BYTE.layout, 0, 0); 86 } 87 88 @Test(expectedExceptions = IndexOutOfBoundsException.class, dataProvider = "types") 89 public void testBadOverflow(Type type) { 90 if (type.layout.byteSize() > 1) { 91 MemorySegment segment = MemorySegment.ofArray(new byte[100]); 92 MemorySegment.copy(segment, type.layout, 0, segment, type.layout, 0, Long.MAX_VALUE); 93 } else { 94 throw new SkipException("Byte layouts do not overflow"); 95 } 96 } 97 98 @Test(dataProvider = "segmentKindsAndTypes") 99 public void testElementCopy(SegmentKind kind1, SegmentKind kind2, Type type1, Type type2) { 100 MemorySegment s1 = kind1.makeSegment(TEST_BYTE_SIZE); 101 MemorySegment s2 = kind2.makeSegment(TEST_BYTE_SIZE); 102 103 // for all offsets 104 for (int s1Offset = 0; s1Offset < s1.byteSize(); s1Offset++) { 105 for (int s2Offset = 0; s2Offset < s2.byteSize(); s2Offset++) { 106 long slice1ByteSize = s1.byteSize() - s1Offset; 107 long slice2ByteSize = s2.byteSize() - s2Offset; 108 109 long slice1ElementSize = slice1ByteSize / type1.size(); 110 long slice2ElementSize = slice2ByteSize / type2.size(); 111 112 long copySize = Math.min(slice1ElementSize, slice2ElementSize); 113 114 //prepare source slice 115 for (int i = 0 ; i < copySize; i++) { 116 type1.set(s1, s1Offset, i, i); 117 } 118 //perform copy 119 MemorySegment.copy(s1, type1.layout, s1Offset, s2, type2.layout, s2Offset, copySize); 120 //check that copy actually worked 121 for (int i = 0; i < copySize; i++) { 122 type2.check(s2, s2Offset, i, i); 123 } 124 } 125 } 126 } 127 128 @Test(expectedExceptions = IllegalArgumentException.class) 129 public void testHyperAlignedSrc() { 130 MemorySegment segment = MemorySegment.ofArray(new byte[] {1, 2, 3, 4}); 131 MemorySegment.copy(segment, 0, segment, JAVA_BYTE.withByteAlignment(2), 0, 4); 132 } 133 134 @Test(expectedExceptions = IllegalArgumentException.class) 135 public void testHyperAlignedDst() { 136 MemorySegment segment = MemorySegment.ofArray(new byte[] {1, 2, 3, 4}); 137 MemorySegment.copy(segment, JAVA_BYTE.withByteAlignment(2), 0, segment, 0, 4); 138 } 139 140 enum Type { 141 // Byte 142 BYTE(byte.class, JAVA_BYTE, i -> (byte)i), 143 //LE 144 SHORT_LE(short.class, ValueLayout.JAVA_SHORT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (short)i), 145 CHAR_LE(char.class, ValueLayout.JAVA_CHAR_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (char)i), 146 INT_LE(int.class, ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN), i -> i), 147 FLOAT_LE(float.class, ValueLayout.JAVA_FLOAT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (float)i), 148 LONG_LE(long.class, ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (long)i), 149 DOUBLE_LE(double.class, ValueLayout.JAVA_DOUBLE_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN), i -> (double)i), 150 //BE 151 SHORT_BE(short.class, ValueLayout.JAVA_SHORT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN), i -> (short)i), 152 CHAR_BE(char.class, ValueLayout.JAVA_CHAR_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN), i -> (char)i), 153 INT_BE(int.class, ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN), i -> i), 154 FLOAT_BE(float.class, ValueLayout.JAVA_FLOAT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN), i -> (float)i), 155 LONG_BE(long.class, ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN), i -> (long)i), 156 DOUBLE_BE(double.class, ValueLayout.JAVA_DOUBLE_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN), i -> (double)i); 157 158 final ValueLayout layout; 159 final IntFunction<Object> valueConverter; 160 final Class<?> carrier; 161 162 @SuppressWarnings("unchecked") 163 <Z> Type(Class<Z> carrier, ValueLayout layout, IntFunction<Z> valueConverter) { 164 this.carrier = carrier; 165 this.layout = layout; 166 this.valueConverter = (IntFunction<Object>)valueConverter; 167 } 168 169 long size() { 170 return layout.byteSize(); 171 } 172 173 VarHandle handle() { 174 return MethodHandles.memorySegmentViewVarHandle(layout); 175 } 176 177 void set(MemorySegment segment, long offset, int index, int val) { 178 handle().set(segment, offset + (index * size()), valueConverter.apply(val)); 179 } 180 181 void check(MemorySegment segment, long offset, int index, int val) { 182 assertEquals(handle().get(segment, offset + (index * size())), valueConverter.apply(val)); 183 } 184 } 185 186 enum SegmentKind { 187 NATIVE(i -> Arena.ofAuto().allocate(i, 1)), 188 ARRAY(i -> MemorySegment.ofArray(new byte[i])); 189 190 final IntFunction<MemorySegment> segmentFactory; 191 192 SegmentKind(IntFunction<MemorySegment> segmentFactory) { 193 this.segmentFactory = segmentFactory; 194 } 195 196 MemorySegment makeSegment(int size) { 197 return segmentFactory.apply(size); 198 } 199 } 200 201 @DataProvider 202 static Object[][] segmentKinds() { 203 List<Object[]> cases = new ArrayList<>(); 204 for (SegmentKind kind1 : SegmentKind.values()) { 205 for (SegmentKind kind2 : SegmentKind.values()) { 206 cases.add(new Object[] {kind1, kind2}); 207 } 208 } 209 return cases.toArray(Object[][]::new); 210 } 211 212 @DataProvider 213 static Object[][] types() { 214 return Arrays.stream(Type.values()) 215 .map(t -> new Object[] { t }) 216 .toArray(Object[][]::new); 217 } 218 219 @DataProvider 220 static Object[][] segmentKindsAndTypes() { 221 List<Object[]> cases = new ArrayList<>(); 222 for (Object[] segmentKinds : segmentKinds()) { 223 for (Type type1 : Type.values()) { 224 for (Type type2 : Type.values()) { 225 if (type1.layout.carrier() == type2.layout.carrier()) { 226 cases.add(new Object[]{segmentKinds[0], segmentKinds[1], type1, type2}); 227 } 228 } 229 } 230 } 231 return cases.toArray(Object[][]::new); 232 } 233 }