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 package test.java.nio.Buffer;
25 
26 import java.lang.invoke.MethodHandle;
27 import java.lang.invoke.MethodHandles;
28 import java.lang.invoke.MethodType;
29 import java.nio.Buffer;
30 import java.nio.ByteBuffer;
31 import java.nio.ByteOrder;
32 import java.nio.CharBuffer;
33 import java.nio.DoubleBuffer;
34 import java.nio.FloatBuffer;
35 import java.nio.IntBuffer;
36 import java.nio.LongBuffer;
37 import java.nio.ReadOnlyBufferException;
38 import java.nio.ShortBuffer;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Random;
46 
47 import org.junit.runner.RunWith;
48 import org.junit.runners.Parameterized;
49 import org.testng.Assert;
50 import org.testng.annotations.DataProvider;
51 import org.testng.annotations.Test;
52 
53 
54 /*
55  * @test
56  * @bug 8219014 8245121
57  * @summary Ensure that a bulk put of a buffer into another is correct.
58  * @compile --enable-preview -source ${jdk.version} BulkPutBuffer.java
59  * @run testng/othervm --enable-preview BulkPutBuffer
60  */
61 public class BulkPutBuffer {
62     static final long SEED = System.nanoTime();
63     static final MyRandom RND = new MyRandom(SEED);
64 
65     static final int ITERATIONS = 100;
66     static final int MAX_CAPACITY = 1024;
67 
68     static class MyRandom extends Random {
MyRandom(long seed)69         MyRandom(long seed) {
70             super(seed);
71         }
72 
nextByte()73         public byte nextByte() {
74             return (byte)next(8);
75         }
76 
nextChar()77         public char nextChar() {
78             return (char)next(16);
79         }
80 
nextShort()81         public short nextShort() {
82             return (short)next(16);
83         }
84     }
85 
86     enum BufferKind {
87         HEAP,
88         HEAP_VIEW,
89         DIRECT,
90         STRING;
91     }
92 
93     static final Map<Class<?>,TypeAttr> typeToAttr;
94 
TypeAttr(Class<?> type, int bytes, String name)95     static record TypeAttr(Class<?> type, int bytes, String name) {}
96 
97     static {
98         typeToAttr = Map.of(
99             byte.class, new TypeAttr(ByteBuffer.class, Byte.BYTES, "Byte"),
100             char.class, new TypeAttr(CharBuffer.class, Character.BYTES, "Char"),
101             short.class, new TypeAttr(ShortBuffer.class, Short.BYTES, "Short"),
102             int.class, new TypeAttr(IntBuffer.class, Integer.BYTES, "Int"),
103             float.class, new TypeAttr(FloatBuffer.class, Float.BYTES, "Float"),
104             long.class, new TypeAttr(LongBuffer.class, Long.BYTES, "Long"),
105             double.class, new TypeAttr(DoubleBuffer.class, Double.BYTES, "Double")
106         );
107     }
108 
getKinds(Class<?> elementType)109     static BufferKind[] getKinds(Class<?> elementType) {
110         BufferKind[] kinds;
111         if (elementType == byte.class)
112             kinds = new BufferKind[] {
113                 BufferKind.DIRECT,
114                 BufferKind.HEAP
115             };
116         else if (elementType == char.class)
117             kinds = BufferKind.values();
118         else
119             kinds = new BufferKind[] {
120                 BufferKind.DIRECT,
121                 BufferKind.HEAP,
122                 BufferKind.HEAP_VIEW
123             };
124         return kinds;
125     }
126 
getOrders(BufferKind kind, Class<?> elementType)127     static ByteOrder[] getOrders(BufferKind kind, Class<?> elementType) {
128         switch (kind) {
129             case HEAP:
130                 return new ByteOrder[] { ByteOrder.nativeOrder() };
131             default:
132                 if (elementType == byte.class)
133                     return new ByteOrder[] { ByteOrder.nativeOrder() };
134                 else
135                     return new ByteOrder[] { ByteOrder.BIG_ENDIAN,
136                         ByteOrder.LITTLE_ENDIAN };
137         }
138     }
139 
140     public static class BufferProxy {
141         final Class<?> elementType;
142         final BufferKind kind;
143         final ByteOrder order;
144 
145         // Buffer methods
146         MethodHandle alloc;
147         MethodHandle allocBB;
148         MethodHandle allocDirect;
149         MethodHandle asReadOnlyBuffer;
150         MethodHandle asTypeBuffer;
151         MethodHandle putAbs;
152         MethodHandle getAbs;
153         MethodHandle putBufAbs;
154         MethodHandle putBufRel;
155         MethodHandle equals;
156 
157         // MyRandom method
158         MethodHandle nextType;
159 
BufferProxy(Class<?> elementType, BufferKind kind, ByteOrder order)160         BufferProxy(Class<?> elementType, BufferKind kind, ByteOrder order) {
161             this.elementType = elementType;
162             this.kind = kind;
163             this.order = order;
164 
165             Class<?> bufferType = typeToAttr.get(elementType).type;
166 
167             var lookup = MethodHandles.lookup();
168             try {
169                 String name = typeToAttr.get(elementType).name;
170 
171                 alloc = lookup.findStatic(bufferType, "allocate",
172                     MethodType.methodType(bufferType, int.class));
173                 allocBB = lookup.findStatic(ByteBuffer.class, "allocate",
174                     MethodType.methodType(ByteBuffer.class, int.class));
175                 allocDirect = lookup.findStatic(ByteBuffer.class, "allocateDirect",
176                     MethodType.methodType(ByteBuffer.class, int.class));
177 
178                 asReadOnlyBuffer = lookup.findVirtual(bufferType,
179                         "asReadOnlyBuffer", MethodType.methodType(bufferType));
180                 if (elementType != byte.class) {
181                     asTypeBuffer = lookup.findVirtual(ByteBuffer.class,
182                         "as" + name + "Buffer", MethodType.methodType(bufferType));
183                 }
184 
185                 putAbs = lookup.findVirtual(bufferType, "put",
186                     MethodType.methodType(bufferType, int.class, elementType));
187                 getAbs = lookup.findVirtual(bufferType, "get",
188                     MethodType.methodType(elementType, int.class));
189 
190                 putBufAbs = lookup.findVirtual(bufferType, "put",
191                     MethodType.methodType(bufferType, int.class, bufferType,
192                         int.class, int.class));
193                 putBufRel = lookup.findVirtual(bufferType, "put",
194                     MethodType.methodType(bufferType, bufferType));
195 
196                 equals = lookup.findVirtual(bufferType, "equals",
197                     MethodType.methodType(boolean.class, Object.class));
198 
199                 nextType = lookup.findVirtual(MyRandom.class,
200                      "next" + name, MethodType.methodType(elementType));
201             } catch (IllegalAccessException | NoSuchMethodException e) {
202                 throw new AssertionError(e);
203             }
204         }
205 
create(int capacity)206         Buffer create(int capacity) throws Throwable {
207 
208             Class<?> bufferType = typeToAttr.get(elementType).type;
209 
210             try {
211                 if (bufferType == ByteBuffer.class ||
212                     kind == BufferKind.DIRECT || kind == BufferKind.HEAP_VIEW) {
213                     int len = capacity*typeToAttr.get(elementType).bytes;
214                     ByteBuffer bb = (ByteBuffer)allocBB.invoke(len);
215                     byte[] bytes = new byte[len];
216                     RND.nextBytes(bytes);
217                     bb.put(0, bytes);
218                     if (bufferType == ByteBuffer.class) {
219                         return (Buffer)bb;
220                     } else {
221                         bb.order(order);
222                         return (Buffer)asTypeBuffer.invoke(bb);
223                     }
224                 } else if (bufferType == CharBuffer.class &&
225                     kind == BufferKind.STRING) {
226                     char[] array = new char[capacity];
227                     for (int i = 0; i < capacity; i++) {
228                         array[i] = RND.nextChar();
229                     }
230                     return CharBuffer.wrap(new String(array));
231                 } else {
232                     Buffer buf = (Buffer)alloc.invoke(capacity);
233                     for (int i = 0; i < capacity; i++) {
234                         putAbs.invoke(buf, i, nextType.invoke(RND));
235                     }
236                     return buf;
237                 }
238             } catch (Exception e) {
239                 throw new AssertionError(e);
240             }
241         }
242 
copy(Buffer src, int srcOff, Buffer dst, int dstOff, int length)243         void copy(Buffer src, int srcOff, Buffer dst, int dstOff, int length)
244             throws Throwable {
245             try {
246                 for (int i = 0; i < length; i++) {
247                     putAbs.invoke(dst, dstOff + i, getAbs.invoke(src, srcOff + i));
248                 }
249             } catch (ReadOnlyBufferException ro) {
250                 throw ro;
251             } catch (Exception e) {
252                 throw new AssertionError(e);
253             }
254         }
255 
asReadOnlyBuffer(Buffer buf)256         Buffer asReadOnlyBuffer(Buffer buf) throws Throwable {
257             try {
258                 return (Buffer)asReadOnlyBuffer.invoke(buf);
259             } catch (Exception e) {
260                 throw new AssertionError(e);
261             }
262         }
263 
put(Buffer src, int srcOff, Buffer dst, int dstOff, int length)264         void put(Buffer src, int srcOff, Buffer dst, int dstOff, int length)
265             throws Throwable {
266             try {
267                 putBufAbs.invoke(dst, dstOff, src, srcOff, length);
268             } catch (ReadOnlyBufferException ro) {
269                 throw ro;
270             } catch (Exception e) {
271                 throw new AssertionError(e);
272             }
273         }
274 
put(Buffer src, Buffer dst)275         void put(Buffer src, Buffer dst) throws Throwable {
276             try {
277                 putBufRel.invoke(dst, src);
278             } catch (ReadOnlyBufferException ro) {
279                 throw ro;
280             } catch (Exception e) {
281                 throw new AssertionError(e);
282             }
283         }
284 
equals(Buffer src, Buffer dst)285         boolean equals(Buffer src, Buffer dst) throws Throwable {
286             try {
287                 return Boolean.class.cast(equals.invoke(dst, src));
288             } catch (Exception e) {
289                 throw new AssertionError(e);
290             }
291         }
292 
293         // Android-added: Add toString() method for splitting BulkPutBufferPairTest.
294         @Override
toString()295         public String toString() {
296             return elementType.getSimpleName() + "-" + kind + "-" +
297                     (order == ByteOrder.LITTLE_ENDIAN ? "L" : "B");
298         }
299     }
300 
getProxies(Class<?> type)301     static List<BufferProxy> getProxies(Class<?> type) {
302         List proxies = new ArrayList();
303         for (BufferKind kind : getKinds(type)) {
304             for (ByteOrder order : getOrders(kind, type)) {
305                 proxies.add(new BufferProxy(type, kind, order));
306             }
307         }
308         return proxies;
309     }
310 
311     @DataProvider
proxies()312     static Object[][] proxies() {
313         ArrayList<Object[]> args = new ArrayList<>();
314         for (Class<?> type : typeToAttr.keySet()) {
315             List<BufferProxy> proxies = getProxies(type);
316             for (BufferProxy proxy : proxies) {
317                 args.add(new Object[] {proxy});
318             }
319         }
320         return args.toArray(Object[][]::new);
321     }
322 
323     @DataProvider
proxyPairs()324     static Object[][] proxyPairs() {
325         List<Object[]> args = new ArrayList<>();
326         for (Class<?> type : typeToAttr.keySet()) {
327             List<BufferProxy> proxies = getProxies(type);
328             for (BufferProxy proxy1 : proxies) {
329                 for (BufferProxy proxy2 : proxies) {
330                     args.add(new Object[] {proxy1, proxy2});
331                 }
332             }
333         }
334         return args.toArray(Object[][]::new);
335     }
336 
expectThrows(Class<?> exClass, Assert.ThrowingRunnable r)337     private static void expectThrows(Class<?> exClass, Assert.ThrowingRunnable r) {
338         try {
339             r.run();
340         } catch(Throwable e) {
341             if (e.getClass() != exClass && e.getCause().getClass() != exClass) {
342                 throw new RuntimeException("Expected " + exClass +
343                 "; got " + e.getCause().getClass(), e);
344             }
345         }
346     }
347 
348     @Test(dataProvider = "proxies")
testExceptions(BufferProxy bp)349     public static void testExceptions(BufferProxy bp) throws Throwable {
350         int cap = 27;
351         Buffer buf = bp.create(cap);
352 
353         expectThrows(IndexOutOfBoundsException.class,
354             () -> bp.put(buf, -1, buf, 0, 1));
355         expectThrows(IndexOutOfBoundsException.class,
356             () -> bp.put(buf, 0, buf, -1, 1));
357         expectThrows(IndexOutOfBoundsException.class,
358             () -> bp.put(buf, 1, buf, 0, cap));
359         expectThrows(IndexOutOfBoundsException.class,
360             () -> bp.put(buf, 0, buf, 1, cap));
361         expectThrows(IndexOutOfBoundsException.class,
362             () -> bp.put(buf, 0, buf, 0, cap + 1));
363         expectThrows(IndexOutOfBoundsException.class,
364             () -> bp.put(buf, 0, buf, 0, Integer.MAX_VALUE));
365 
366         Buffer rob = buf.isReadOnly() ? buf : bp.asReadOnlyBuffer(buf);
367         expectThrows(ReadOnlyBufferException.class,
368             () -> bp.put(buf, 0, rob, 0, cap));
369     }
370 
371     @Test(dataProvider = "proxies")
testSelf(BufferProxy bp)372     public static void testSelf(BufferProxy bp) throws Throwable {
373         for (int i = 0; i < ITERATIONS; i++) {
374             int cap = RND.nextInt(MAX_CAPACITY);
375             Buffer buf = bp.create(cap);
376 
377             int lowerOffset = RND.nextInt(1 + cap/10);
378             int lowerLength = RND.nextInt(1 + cap/2);
379             if (lowerLength < 2)
380                 continue;
381             Buffer lower = buf.slice(lowerOffset, lowerLength);
382 
383             Buffer lowerCopy = bp.create(lowerLength);
384             if (lowerCopy.isReadOnly()) {
385                 Assert.expectThrows(ReadOnlyBufferException.class,
386                     () -> bp.copy(lower, 0, lowerCopy, 0, lowerLength));
387                 break;
388             }
389             bp.copy(lower, 0, lowerCopy, 0, lowerLength);
390 
391             int middleOffset = RND.nextInt(1 + cap/2);
392             Buffer middle = buf.slice(middleOffset, lowerLength);
393             Buffer middleCopy = bp.create(lowerLength);
394             bp.copy(middle, 0, middleCopy, 0, lowerLength);
395 
396             bp.put(lower, middle);
397             middle.flip();
398 
399             Assert.assertTrue(bp.equals(lowerCopy, middle),
400                 String.format("%d %s %d %d %d %d%n", SEED,
401                     buf.getClass().getName(), cap,
402                     lowerOffset, lowerLength, middleOffset));
403 
404             bp.copy(lowerCopy, 0, buf, lowerOffset, lowerLength);
405             bp.copy(middleCopy, 0, buf, middleOffset, lowerLength);
406 
407             bp.put(buf, lowerOffset, buf, middleOffset, lowerLength);
408 
409             Assert.assertTrue(bp.equals(lowerCopy, middle),
410                 String.format("%d %s %d %d %d %d%n", SEED,
411                     buf.getClass().getName(), cap,
412                     lowerOffset, lowerLength, middleOffset));
413         }
414     }
415 
416     // Android-changed: Remove @Test and move the test invocations to PairsTest.
417     // @Test(dataProvider = "proxyPairs")
testPairs(BufferProxy bp, BufferProxy sbp)418     static void testPairs(BufferProxy bp, BufferProxy sbp) throws Throwable {
419         for (int i = 0; i < ITERATIONS; i++) {
420             int cap = Math.max(4, RND.nextInt(MAX_CAPACITY));
421             int cap2 = cap/2;
422             Buffer buf = bp.create(cap);
423 
424             int pos = RND.nextInt(Math.max(1, cap2));
425             buf.position(pos);
426             buf.mark();
427             int lim = pos + Math.max(1, cap - pos);
428             buf.limit(lim);
429 
430             int scap = Math.max(buf.remaining(), RND.nextInt(1024));
431             Buffer src = sbp.create(scap);
432 
433             int diff = scap - buf.remaining();
434             int spos = diff > 0 ? RND.nextInt(diff) : 0;
435             src.position(spos);
436             src.mark();
437             int slim = spos + buf.remaining();
438             src.limit(slim);
439 
440             if (buf.isReadOnly()) {
441                 Assert.expectThrows(ReadOnlyBufferException.class,
442                     () -> bp.put(src, buf));
443                 break;
444             }
445 
446             Buffer backup = bp.create(slim - spos);
447             bp.copy(buf, pos, backup, 0, backup.capacity());
448             bp.put(src, buf);
449 
450             buf.reset();
451             src.reset();
452 
453             Assert.assertTrue(bp.equals(src, buf),
454                 String.format("%d %s %d %d %d %s %d %d %d%n", SEED,
455                     buf.getClass().getName(), cap, pos, lim,
456                     src.getClass().getName(), scap, spos, slim));
457 
458             src.clear();
459             buf.clear();
460             bp.copy(backup, 0, buf, pos, backup.capacity());
461             bp.put(src, spos, buf, pos, backup.capacity());
462             src.position(spos);
463             src.limit(slim);
464             buf.position(pos);
465             buf.limit(lim);
466 
467             Assert.assertTrue(bp.equals(src, buf),
468                 String.format("%d %s %d %d %d %s %d %d %d%n", SEED,
469                     buf.getClass().getName(), cap, pos, lim,
470                     src.getClass().getName(), scap, spos, slim));
471         }
472     }
473 }
474 
475