1 /*
  2  *  Copyright (c) 2020, 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 TestMismatch
 27  */
 28 
 29 import java.lang.invoke.VarHandle;
 30 import java.util.ArrayList;
 31 import java.util.List;
 32 import java.util.concurrent.atomic.AtomicReference;
 33 import java.util.function.IntFunction;
 34 
 35 import jdk.incubator.foreign.MemorySegment;
 36 import jdk.incubator.foreign.ResourceScope;
 37 import jdk.incubator.foreign.ValueLayout;
 38 import org.testng.annotations.DataProvider;
 39 import org.testng.annotations.Test;
 40 import static java.lang.System.out;
 41 import static org.testng.Assert.assertEquals;
 42 import static org.testng.Assert.assertThrows;
 43 
 44 public class TestMismatch {
 45 
 46     final static VarHandle BYTE_HANDLE = ValueLayout.JAVA_BYTE.varHandle();
 47 
 48     // stores a increasing sequence of values into the memory of the given segment
 49     static MemorySegment initializeSegment(MemorySegment segment) {
 50         for (int i = 0 ; i < segment.byteSize() ; i++) {
 51             BYTE_HANDLE.set(segment.asSlice(i), (byte)i);
 52         }
 53         return segment;
 54     }
 55 
 56     @Test(dataProvider = "slices")
 57     public void testSameValues(MemorySegment ss1, MemorySegment ss2) {
 58         out.format("testSameValues s1:%s, s2:%s\n", ss1, ss2);
 59         MemorySegment s1 = initializeSegment(ss1);
 60         MemorySegment s2 = initializeSegment(ss2);
 61 
 62         if (s1.byteSize() == s2.byteSize()) {
 63             assertEquals(s1.mismatch(s2), -1);  // identical
 64             assertEquals(s2.mismatch(s1), -1);
 65         } else if (s1.byteSize() > s2.byteSize()) {
 66             assertEquals(s1.mismatch(s2), s2.byteSize());  // proper prefix
 67             assertEquals(s2.mismatch(s1), s2.byteSize());
 68         } else {
 69             assert s1.byteSize() < s2.byteSize();
 70             assertEquals(s1.mismatch(s2), s1.byteSize());  // proper prefix
 71             assertEquals(s2.mismatch(s1), s1.byteSize());
 72         }
 73     }
 74 
 75     @Test(dataProvider = "slices")
 76     public void testDifferentValues(MemorySegment s1, MemorySegment s2) {
 77         out.format("testDifferentValues s1:%s, s2:%s\n", s1, s2);
 78         s1 = initializeSegment(s1);
 79         s2 = initializeSegment(s2);
 80 
 81         for (long i = s2.byteSize() -1 ; i >= 0; i--) {
 82             long expectedMismatchOffset = i;
 83             BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF);
 84 
 85             if (s1.byteSize() == s2.byteSize()) {
 86                 assertEquals(s1.mismatch(s2), expectedMismatchOffset);
 87                 assertEquals(s2.mismatch(s1), expectedMismatchOffset);
 88             } else if (s1.byteSize() > s2.byteSize()) {
 89                 assertEquals(s1.mismatch(s2), expectedMismatchOffset);
 90                 assertEquals(s2.mismatch(s1), expectedMismatchOffset);
 91             } else {
 92                 assert s1.byteSize() < s2.byteSize();
 93                 var off = Math.min(s1.byteSize(), expectedMismatchOffset);
 94                 assertEquals(s1.mismatch(s2), off);  // proper prefix
 95                 assertEquals(s2.mismatch(s1), off);
 96             }
 97         }
 98     }
 99 
100     @Test
101     public void testEmpty() {
102         var s1 = MemorySegment.ofArray(new byte[0]);
103         assertEquals(s1.mismatch(s1), -1);
104         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
105             var nativeSegment = MemorySegment.allocateNative(4, 4, scope);
106             var s2 = nativeSegment.asSlice(0, 0);
107             assertEquals(s1.mismatch(s2), -1);
108             assertEquals(s2.mismatch(s1), -1);
109         }
110     }
111 
112     @Test
113     public void testLarge() {
114         // skip if not on 64 bits
115         if (ValueLayout.ADDRESS.byteSize() > 32) {
116             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
117                 var s1 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, scope);
118                 var s2 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L, 8, scope);
119                 assertEquals(s1.mismatch(s1), -1);
120                 assertEquals(s1.mismatch(s2), -1);
121                 assertEquals(s2.mismatch(s1), -1);
122 
123                 testLargeAcrossMaxBoundary(s1, s2);
124 
125                 testLargeMismatchAcrossMaxBoundary(s1, s2);
126             }
127         }
128     }
129 
130     private void testLargeAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) {
131         for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) {
132             var s3 = s1.asSlice(0, i);
133             var s4 = s2.asSlice(0, i);
134             assertEquals(s3.mismatch(s3), -1);
135             assertEquals(s3.mismatch(s4), -1);
136             assertEquals(s4.mismatch(s3), -1);
137         }
138     }
139 
140     private void testLargeMismatchAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) {
141         for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) {
142             BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF);
143             long expectedMismatchOffset = i;
144             assertEquals(s1.mismatch(s2), expectedMismatchOffset);
145             assertEquals(s2.mismatch(s1), expectedMismatchOffset);
146         }
147     }
148 
149     static final Class<IllegalStateException> ISE = IllegalStateException.class;
150     static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
151 
152     @Test
153     public void testClosed() {
154         MemorySegment s1, s2;
155         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
156             s1 = MemorySegment.allocateNative(4, 1, scope);
157             s2 = MemorySegment.allocateNative(4, 1, scope);
158         }
159         assertThrows(ISE, () -> s1.mismatch(s1));
160         assertThrows(ISE, () -> s1.mismatch(s2));
161         assertThrows(ISE, () -> s2.mismatch(s1));
162     }
163 
164     @Test
165     public void testThreadAccess() throws Exception {
166         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
167             var segment = MemorySegment.allocateNative(4, 1, scope);
168             {
169                 AtomicReference<RuntimeException> exception = new AtomicReference<>();
170                 Runnable action = () -> {
171                     try {
172                         MemorySegment.ofArray(new byte[4]).mismatch(segment);
173                     } catch (RuntimeException e) {
174                         exception.set(e);
175                     }
176                 };
177                 Thread thread = new Thread(action);
178                 thread.start();
179                 thread.join();
180 
181                 RuntimeException e = exception.get();
182                 if (!(e instanceof IllegalStateException)) {
183                     throw e;
184                 }
185             }
186             {
187                 AtomicReference<RuntimeException> exception = new AtomicReference<>();
188                 Runnable action = () -> {
189                     try {
190                         segment.mismatch(MemorySegment.ofArray(new byte[4]));
191                     } catch (RuntimeException e) {
192                         exception.set(e);
193                     }
194                 };
195                 Thread thread = new Thread(action);
196                 thread.start();
197                 thread.join();
198 
199                 RuntimeException e = exception.get();
200                 if (!(e instanceof IllegalStateException)) {
201                     throw e;
202                 }
203             }
204         }
205     }
206 
207     enum SegmentKind {
208         NATIVE(i -> MemorySegment.allocateNative(i, ResourceScope.newImplicitScope())),
209         ARRAY(i -> MemorySegment.ofArray(new byte[i]));
210 
211         final IntFunction<MemorySegment> segmentFactory;
212 
213         SegmentKind(IntFunction<MemorySegment> segmentFactory) {
214             this.segmentFactory = segmentFactory;
215         }
216 
217         MemorySegment makeSegment(int elems) {
218             return segmentFactory.apply(elems);
219         }
220     }
221 
222     @DataProvider(name = "slices")
223     static Object[][] slices() {
224         int[] sizes = { 16, 8, 1 };
225         List<MemorySegment> aSlices = new ArrayList<>();
226         List<MemorySegment> bSlices = new ArrayList<>();
227         for (List<MemorySegment> slices : List.of(aSlices, bSlices)) {
228             for (SegmentKind kind : SegmentKind.values()) {
229                 MemorySegment segment = kind.makeSegment(16);
230                 //compute all slices
231                 for (int size : sizes) {
232                     for (int index = 0 ; index < 16 ; index += size) {
233                         MemorySegment slice = segment.asSlice(index, size);
234                         slices.add(slice);
235                     }
236                 }
237             }
238         }
239         assert aSlices.size() == bSlices.size();
240         Object[][] sliceArray = new Object[aSlices.size() * bSlices.size()][];
241         for (int i = 0 ; i < aSlices.size() ; i++) {
242             for (int j = 0 ; j < bSlices.size() ; j++) {
243                 sliceArray[i * aSlices.size() + j] = new Object[] { aSlices.get(i), bSlices.get(j) };
244             }
245         }
246         return sliceArray;
247     }
248 }