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 import java.lang.invoke.MethodHandle; 25 import java.lang.invoke.MethodHandles; 26 import java.lang.invoke.MethodType; 27 import java.nio.Buffer; 28 import java.nio.ByteBuffer; 29 import java.nio.CharBuffer; 30 import java.nio.ShortBuffer; 31 import java.nio.IntBuffer; 32 import java.nio.LongBuffer; 33 import java.nio.FloatBuffer; 34 import java.nio.DoubleBuffer; 35 import java.nio.ByteOrder; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.HashMap; 39 import java.util.Iterator; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Random; 43 44 import org.testng.Assert; 45 import org.testng.annotations.DataProvider; 46 import org.testng.annotations.Test; 47 48 49 /* 50 * @test 51 * @bug 8245121 52 * @summary Ensure that a bulk put of a buffer into another is correct. 53 * @compile --enable-preview -source ${jdk.version} BulkPutBuffer.java 54 * @run testng/othervm --enable-preview BulkPutBuffer 55 */ 56 public class BulkPutBuffer { 57 static final long SEED = System.nanoTime(); 58 static final Random RND = new MyRandom(SEED); 59 60 static final int ITERATIONS = 100; 61 static final int MAX_CAPACITY = 1024; 62 63 static class MyRandom extends Random { 64 MyRandom(long seed) { 65 super(seed); 66 } 67 68 public byte nextByte() { 69 return (byte)next(8); 70 } 71 72 public char nextChar() { 73 return (char)next(16); 74 } 75 76 public short nextShort() { 77 return (short)next(16); 78 } 79 } 80 81 enum BufferKind { 82 HEAP, 83 HEAP_VIEW, 84 DIRECT; 85 } 86 87 static final Map<Class<?>,TypeAttr> typeToAttr; 88 89 static record TypeAttr(Class<?> type, int bytes, String name) {} 90 91 static { 92 typeToAttr = Map.of( 93 byte.class, new TypeAttr(ByteBuffer.class, Byte.BYTES, "Byte"), 94 char.class, new TypeAttr(CharBuffer.class, Character.BYTES, "Char"), 95 short.class, new TypeAttr(ShortBuffer.class, Short.BYTES, "Short"), 96 int.class, new TypeAttr(IntBuffer.class, Integer.BYTES, "Int"), 97 float.class, new TypeAttr(FloatBuffer.class, Float.BYTES, "Float"), 98 long.class, new TypeAttr(LongBuffer.class, Long.BYTES, "Long"), 99 double.class, new TypeAttr(DoubleBuffer.class, Double.BYTES, "Double") 100 ); 101 } 102 103 static BufferKind[] getKinds(Class<?> elementType) { 104 return elementType == byte.class ? 105 new BufferKind[] { BufferKind.DIRECT, BufferKind.HEAP } : 106 BufferKind.values(); 107 } 108 109 static ByteOrder[] getOrders(BufferKind kind, Class<?> elementType) { 110 switch (kind) { 111 case HEAP: 112 return new ByteOrder[] { ByteOrder.nativeOrder() }; 113 default: 114 if (elementType == byte.class) 115 return new ByteOrder[] { ByteOrder.nativeOrder() }; 116 else 117 return new ByteOrder[] { ByteOrder.BIG_ENDIAN, 118 ByteOrder.LITTLE_ENDIAN }; 119 } 120 } 121 122 public static class BufferProxy { 123 final Class<?> elementType; 124 final BufferKind kind; 125 final ByteOrder order; 126 127 // Buffer methods 128 MethodHandle alloc; 129 MethodHandle allocBB; 130 MethodHandle allocDirect; 131 MethodHandle asTypeBuffer; 132 MethodHandle putAbs; 133 MethodHandle getAbs; 134 MethodHandle putBufRel; 135 MethodHandle equals; 136 137 // MyRandom method 138 MethodHandle nextType; 139 140 BufferProxy(Class<?> elementType, BufferKind kind, ByteOrder order) { 141 this.elementType = elementType; 142 this.kind = kind; 143 this.order = order; 144 145 Class<?> bufferType = typeToAttr.get(elementType).type; 146 147 var lookup = MethodHandles.lookup(); 148 try { 149 String name = typeToAttr.get(elementType).name; 150 151 alloc = lookup.findStatic(bufferType, "allocate", 152 MethodType.methodType(bufferType, int.class)); 153 allocBB = lookup.findStatic(ByteBuffer.class, "allocate", 154 MethodType.methodType(ByteBuffer.class, int.class)); 155 allocDirect = lookup.findStatic(ByteBuffer.class, "allocateDirect", 156 MethodType.methodType(ByteBuffer.class, int.class)); 157 if (elementType != byte.class) { 158 asTypeBuffer = lookup.findVirtual(ByteBuffer.class, 159 "as" + name + "Buffer", MethodType.methodType(bufferType)); 160 } 161 162 putAbs = lookup.findVirtual(bufferType, "put", 163 MethodType.methodType(bufferType, int.class, elementType)); 164 getAbs = lookup.findVirtual(bufferType, "get", 165 MethodType.methodType(elementType, int.class)); 166 putBufRel = lookup.findVirtual(bufferType, "put", 167 MethodType.methodType(bufferType, bufferType)); 168 equals = lookup.findVirtual(bufferType, "equals", 169 MethodType.methodType(boolean.class, Object.class)); 170 171 nextType = lookup.findVirtual(MyRandom.class, 172 "next" + name, MethodType.methodType(elementType)); 173 } catch (IllegalAccessException | NoSuchMethodException e) { 174 throw new AssertionError(e); 175 } 176 } 177 178 Buffer create(int capacity) throws Throwable { 179 180 Class<?> bufferType = typeToAttr.get(elementType).type; 181 182 try { 183 if (bufferType == ByteBuffer.class || 184 kind == BufferKind.DIRECT || kind == BufferKind.HEAP_VIEW) { 185 int len = capacity*typeToAttr.get(elementType).bytes; 186 ByteBuffer bb = (ByteBuffer)allocBB.invoke(len); 187 byte[] bytes = new byte[len]; 188 RND.nextBytes(bytes); 189 bb.put(0, bytes); 190 if (bufferType == ByteBuffer.class) { 191 return (Buffer)bb; 192 } else { 193 bb.order(order); 194 return (Buffer)asTypeBuffer.invoke(bb); 195 } 196 } else { 197 Buffer buf = (Buffer)alloc.invoke(capacity); 198 for (int i = 0; i < capacity; i++) { 199 putAbs.invoke(buf, i, nextType.invoke(RND)); 200 } 201 return buf; 202 } 203 } catch (Exception e) { 204 throw new AssertionError(e); 205 } 206 } 207 208 void copy(Buffer src, int srcOff, Buffer dst, int dstOff, int length) 209 throws Throwable { 210 try { 211 for (int i = 0; i < length; i++) { 212 putAbs.invoke(dst, dstOff + i, getAbs.invoke(src, srcOff + i)); 213 } 214 } catch (Exception e) { 215 throw new AssertionError(e); 216 } 217 } 218 219 void put(Buffer src, Buffer dst) throws Throwable { 220 try { 221 putBufRel.invoke(dst, src); 222 } catch (Exception e) { 223 throw new AssertionError(e); 224 } 225 } 226 227 boolean equals(Buffer src, Buffer dst) throws Throwable { 228 try { 229 return Boolean.class.cast(equals.invoke(dst, src)); 230 } catch (Exception e) { 231 throw new AssertionError(e); 232 } 233 } 234 } 235 236 static List<BufferProxy> getProxies(Class<?> type) { 237 List proxies = new ArrayList(); 238 for (BufferKind kind : getKinds(type)) { 239 for (ByteOrder order : getOrders(kind, type)) { 240 proxies.add(new BufferProxy(type, kind, order)); 241 } 242 } 243 return proxies; 244 } 245 246 @DataProvider 247 static Object[][] proxies() { 248 ArrayList<Object[]> args = new ArrayList<>(); 249 for (Class<?> type : typeToAttr.keySet()) { 250 List<BufferProxy> proxies = getProxies(type); 251 for (BufferProxy proxy : proxies) { 252 args.add(new Object[] {proxy}); 253 } 254 } 255 return args.toArray(Object[][]::new); 256 257 } 258 259 @DataProvider 260 static Object[][] proxyPairs() { 261 List<Object[]> args = new ArrayList<>(); 262 for (Class<?> type : typeToAttr.keySet()) { 263 List<BufferProxy> proxies = getProxies(type); 264 for (BufferProxy proxy1 : proxies) { 265 for (BufferProxy proxy2 : proxies) { 266 args.add(new Object[] {proxy1, proxy2}); 267 } 268 } 269 } 270 return args.toArray(Object[][]::new); 271 } 272 273 @Test(dataProvider = "proxies") 274 public static void testSelf(BufferProxy bp) throws Throwable { 275 for (int i = 0; i < ITERATIONS; i++) { 276 int cap = RND.nextInt(MAX_CAPACITY); 277 Buffer buf = bp.create(cap); 278 279 int lowerOffset = RND.nextInt(1 + cap/10); 280 int lowerLength = RND.nextInt(1 + cap/2); 281 if (lowerLength < 2) 282 continue; 283 Buffer lower = buf.slice(lowerOffset, lowerLength); 284 285 Buffer lowerCopy = bp.create(lowerLength); 286 bp.copy(lower, 0, lowerCopy, 0, lowerLength); 287 288 int middleOffset = RND.nextInt(1 + cap/2); 289 Buffer middle = buf.slice(middleOffset, lowerLength); 290 291 bp.put(lower, middle); 292 middle.flip(); 293 294 Assert.assertTrue(bp.equals(lowerCopy, middle), 295 String.format("%d %s %d %d %d %d%n", SEED, 296 buf.getClass().getName(), cap, 297 lowerOffset, lowerLength, middleOffset)); 298 } 299 } 300 301 @Test(dataProvider = "proxyPairs") 302 public static void testPairs(BufferProxy bp, BufferProxy sbp) throws Throwable { 303 for (int i = 0; i < ITERATIONS; i++) { 304 int cap = Math.max(4, RND.nextInt(MAX_CAPACITY)); 305 int cap2 = cap/2; 306 Buffer buf = bp.create(cap); 307 308 int pos = RND.nextInt(Math.max(1, cap2)); 309 buf.position(pos); 310 buf.mark(); 311 int lim = pos + Math.max(1, cap - pos); 312 buf.limit(lim); 313 314 int scap = Math.max(buf.remaining(), RND.nextInt(1024)); 315 Buffer src = sbp.create(scap); 316 317 int diff = scap - buf.remaining(); 318 int spos = diff > 0 ? RND.nextInt(diff) : 0; 319 src.position(spos); 320 src.mark(); 321 int slim = spos + buf.remaining(); 322 src.limit(slim); 323 324 bp.put(src, buf); 325 326 buf.reset(); 327 src.reset(); 328 329 Assert.assertTrue(bp.equals(src, buf), 330 String.format("%d %s %d %d %d %s %d %d %d%n", SEED, 331 buf.getClass().getName(), cap, pos, lim, 332 src.getClass().getName(), scap, spos, slim)); 333 } 334 } 335 }