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