1 /* 2 * Copyright (c) 2002, 2017, 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 /* @test 25 * @summary Comprehensive test for FileChannel.transfer{From,To} 26 * @bug 4708120 27 * @author Mark Reinhold 28 * @run main/timeout=300 Transfers 29 */ 30 31 import java.io.*; 32 import java.nio.*; 33 import java.nio.channels.*; 34 import java.util.*; 35 36 37 public class Transfers { 38 39 static PrintStream out = System.out; 40 41 private static class Failure 42 extends RuntimeException 43 { 44 Failure(Exception x)45 Failure(Exception x) { 46 super(x); 47 } 48 Failure(String s)49 Failure(String s) { 50 super(s); 51 } 52 53 } 54 55 56 // -- Writing and reading random bytes -- 57 writeBytes(byte[] ba, FileChannel fc, int off, int len)58 private static void writeBytes(byte[] ba, FileChannel fc, 59 int off, int len) 60 throws IOException 61 { 62 fc.position(off); 63 if (fc.write(ByteBuffer.wrap(ba, 0, len)) != len) 64 throw new IOException("Incomplete write"); 65 fc.position(0); 66 } 67 writeRandomBytes(long seed, FileChannel fc, int off, int len)68 private static void writeRandomBytes(long seed, 69 FileChannel fc, int off, int len) 70 throws IOException 71 { 72 Random r = new Random(seed); 73 byte[] ba = new byte[len]; 74 r.nextBytes(ba); 75 writeBytes(ba, fc, off, len); 76 } 77 writeZeroBytes(FileChannel fc, int off, int len)78 private static void writeZeroBytes(FileChannel fc, int off, int len) 79 throws IOException 80 { 81 byte[] ba = new byte[len]; 82 writeBytes(ba, fc, off, len); 83 } 84 checkBytes(FileChannel fc, int off, int len, byte[] bytes)85 private static void checkBytes(FileChannel fc, int off, int len, 86 byte[] bytes) 87 throws IOException 88 { 89 ByteBuffer bb = ByteBuffer.allocate(len); 90 fc.position(off); 91 if (fc.read(bb) != len) 92 throw new IOException("Incomplete read"); 93 bb.flip(); 94 ByteBuffer bab = ByteBuffer.wrap(bytes, 0, len); 95 if (!bb.equals(bab)) 96 throw new Failure("Wrong data written"); 97 } 98 checkRandomBytes(FileChannel fc, int off, int len, long seed)99 private static void checkRandomBytes(FileChannel fc, int off, int len, 100 long seed) 101 throws IOException 102 { 103 byte[] ba = new byte[len]; 104 Random r = new Random(seed); 105 r.nextBytes(ba); 106 checkBytes(fc, off, len, ba); 107 } 108 checkZeroBytes(FileChannel fc, int off, int len)109 private static void checkZeroBytes(FileChannel fc, int off, int len) 110 throws IOException 111 { 112 byte[] ba = new byte[len]; 113 checkBytes(fc, off, len, ba); 114 } 115 116 // For debugging 117 // dump(FileChannel fc)118 private static void dump(FileChannel fc) 119 throws IOException 120 { 121 int sz = (int)fc.size(); 122 ByteBuffer bb = ByteBuffer.allocate(sz); 123 fc.position(0); 124 if (fc.read(bb) != sz) 125 throw new IOException("Incomplete read"); 126 bb.flip(); 127 byte prev = -1; 128 int r = 0; // Repeats 129 int n = 0; 130 while (bb.hasRemaining() && (n < 32)) { 131 byte b = bb.get(); 132 if (b == prev) { 133 r++; 134 continue; 135 } 136 if (r > 0) { 137 int c = prev & 0xff; 138 if (c < 0x10) 139 out.print('0'); 140 out.print(Integer.toHexString(c)); 141 if (r > 1) { 142 out.print("["); 143 out.print(r); 144 out.print("]"); 145 } 146 n++; 147 } 148 prev = b; 149 r = 1; 150 } 151 if (r > 0) { 152 int c = prev & 0xff; 153 if (c < 0x10) 154 out.print('0'); 155 out.print(Integer.toHexString(c)); 156 if (r > 1) { 157 out.print("["); 158 out.print(r); 159 out.print("]"); 160 } 161 n++; 162 } 163 if (bb.hasRemaining()) 164 out.print("..."); 165 out.println(); 166 } 167 168 169 170 static File sourceFile; 171 static File targetFile; 172 173 // -- Self-verifying sources and targets -- 174 175 static abstract class Source { 176 177 protected final int size; 178 protected final long seed; 179 private final String name; 180 Source(int size, long seed, String name)181 Source(int size, long seed, String name) { 182 this.size = size; 183 this.seed = seed; 184 this.name = name; 185 } 186 name()187 String name() { 188 return name; 189 } 190 channel()191 abstract ReadableByteChannel channel(); 192 verify()193 abstract void verify() throws IOException; 194 195 } 196 197 static class FileSource 198 extends Source 199 { 200 private final File fn; 201 private final RandomAccessFile raf; 202 private final FileChannel fc; 203 FileSource(int size, long seed)204 FileSource(int size, long seed) throws IOException { 205 super(size, seed, "FileChannel"); 206 fn = sourceFile; 207 raf = new RandomAccessFile(fn, "rw"); 208 fc = raf.getChannel(); 209 fc.position(0); 210 writeRandomBytes(seed, fc, 0, size); 211 } 212 channel()213 ReadableByteChannel channel() { 214 return fc; 215 } 216 verify()217 void verify() throws IOException { 218 if (fc.position() != size) 219 throw new Failure("Wrong position: " 220 + fc.position() + " (expected " + size + 221 ")"); 222 checkRandomBytes(fc, 0, size, seed); 223 fc.close(); 224 raf.close(); // Bug in 1.4.0 225 } 226 227 } 228 229 static class UserSource 230 extends Source 231 { 232 private ReadableByteChannel ch; 233 private final ByteBuffer src; 234 UserSource(int size, long seed)235 UserSource(int size, long seed) { 236 super(size, seed, "UserChannel"); 237 238 final byte[] bytes = new byte[size + 1]; 239 Random r = new Random(seed); 240 r.nextBytes(bytes); 241 src = ByteBuffer.wrap(bytes); 242 243 ch = new ReadableByteChannel() { 244 public int read(ByteBuffer dst) { 245 if (!src.hasRemaining()) 246 return -1; 247 int nr = Math.min(src.remaining(), dst.remaining()); 248 ByteBuffer s = src.duplicate(); 249 s.limit(s.position() + nr); 250 dst.put(s); 251 src.position(src.position() + nr); 252 return nr; 253 } 254 public boolean isOpen() { 255 return true; 256 } 257 public void close() { } 258 }; 259 } 260 channel()261 ReadableByteChannel channel() { 262 return ch; 263 } 264 verify()265 void verify() { 266 if (src.remaining() != 1) 267 throw new Failure("Source has " + src.remaining() 268 + " bytes remaining (expected 1)"); 269 } 270 271 } 272 273 static abstract class Target { 274 275 protected final int size; 276 protected final long seed; 277 private final String name; 278 Target(int size, long seed, String name)279 Target(int size, long seed, String name) { 280 this.size = size; 281 this.seed = seed; 282 this.name = name; 283 } 284 name()285 String name() { 286 return name; 287 } 288 channel()289 abstract WritableByteChannel channel(); 290 verify()291 abstract void verify() throws IOException; 292 293 } 294 295 static class FileTarget 296 extends Target 297 { 298 private final File fn; 299 private final RandomAccessFile raf; 300 private final FileChannel fc; 301 FileTarget(int size, long seed)302 FileTarget(int size, long seed) throws IOException { 303 super(size, seed, "FileChannel"); 304 fn = targetFile; 305 raf = new RandomAccessFile(fn, "rw"); 306 fc = raf.getChannel(); 307 fc.position(0); 308 } 309 channel()310 WritableByteChannel channel() { 311 return fc; 312 } 313 verify()314 void verify() throws IOException { 315 if (fc.position() != size) 316 throw new Failure("Wrong position: " 317 + fc.position() + " (expected " + size + ")"); 318 checkRandomBytes(fc, 0, size, seed); 319 fc.close(); 320 raf.close(); // Bug in 1.4.0 321 } 322 323 } 324 325 static class UserTarget 326 extends Target 327 { 328 private WritableByteChannel ch; 329 private final ByteBuffer dst; 330 UserTarget(int size, long seed)331 UserTarget(int size, long seed) { 332 super(size, seed, "UserChannel"); 333 dst = ByteBuffer.wrap(new byte[size + 1]); 334 335 ch = new WritableByteChannel() { 336 public int write(ByteBuffer src) { 337 int nr = Math.min(src.remaining(), dst.remaining()); 338 ByteBuffer s = src.duplicate(); 339 s.limit(s.position() + nr); 340 dst.put(s); 341 src.position(src.position() + nr); 342 return nr; 343 } 344 public boolean isOpen() { 345 return true; 346 } 347 public void close() { } 348 }; 349 } 350 channel()351 WritableByteChannel channel() { 352 return ch; 353 } 354 verify()355 void verify() { 356 if (dst.remaining() != 1) 357 throw new Failure("Destination has " + dst.remaining() 358 + " bytes remaining (expected 1)"); 359 byte[] ba = new byte[size]; 360 Random r = new Random(seed); 361 r.nextBytes(ba); 362 dst.flip(); 363 ByteBuffer rbb = ByteBuffer.wrap(ba, 0, size); 364 if (!dst.equals(rbb)) 365 throw new Failure("Wrong data written"); 366 } 367 368 } 369 370 371 // Generates a sequence of ints of the form 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 372 // 15, 16, 17, 31, 32, 33, ..., 2^i-1, 2^i, 2^i+1, ..., max. 373 374 static class IntGenerator { 375 376 private int max; 377 private int cur = -1; 378 private int p2 = 8; 379 IntGenerator(int max)380 IntGenerator(int max) { 381 this.max = max; 382 } 383 hasNext()384 boolean hasNext() { 385 return cur < max; 386 } 387 next()388 int next() { 389 if (cur >= max) 390 throw new IllegalStateException(); 391 if (cur < 6) { 392 cur++; 393 return cur; 394 } 395 if (cur == p2 + 1) { 396 p2 <<= 1; 397 cur = p2 - 1; 398 return cur; 399 } 400 cur++; 401 return cur; 402 } 403 404 } 405 406 407 // -- Tests -- 408 409 private static final int MAX_XFER_SIZE = 1 << 14; 410 private static final int MAX_FILE_SIZE = MAX_XFER_SIZE << 1; 411 412 private static boolean debug = false; 413 private static boolean verbose = false; 414 show(String dir, String channelName, int off, int len)415 static void show(String dir, String channelName, int off, int len) { 416 if (!verbose) 417 return; 418 out.println(dir + " " + channelName + 419 ": offset " + off + ", length " + len); 420 } 421 testTo(long seed, FileChannel fc, int off, int len, Target tgt)422 static void testTo(long seed, FileChannel fc, int off, int len, Target tgt) 423 throws IOException 424 { 425 show("To", tgt.name(), off, len); 426 427 // Clear source, then randomize just the source region 428 writeZeroBytes(fc, 0, MAX_FILE_SIZE); 429 writeRandomBytes(seed, fc, off, len); 430 431 // Randomize position 432 int pos = (int)seed & 0xfff; 433 fc.position(pos); 434 435 int n = (int)fc.transferTo(off, len, tgt.channel()); 436 if (n != len) 437 throw new Failure("Incorrect transfer length: " + n 438 + " (expected " + len + ")"); 439 440 // Check that source wasn't changed 441 if (fc.position() != pos) 442 throw new Failure("Position changed"); 443 if (debug) 444 dump(fc); 445 checkRandomBytes(fc, off, len, seed); 446 writeZeroBytes(fc, off, len); 447 checkZeroBytes(fc, 0, MAX_FILE_SIZE); 448 449 // Check that target was updated correctly 450 tgt.verify(); 451 } 452 testFrom(long seed, Source src, FileChannel fc, int off, int len)453 static void testFrom(long seed, Source src, FileChannel fc, int off, int len) 454 throws IOException 455 { 456 show("From", src.name(), off, len); 457 458 // Clear target 459 writeZeroBytes(fc, 0, MAX_FILE_SIZE); 460 461 // Randomize position 462 int pos = (int)seed & 0xfff; 463 fc.position(pos); 464 465 int n = (int)fc.transferFrom(src.channel(), off, len); 466 if (n != len) 467 throw new Failure("Incorrect transfer length: " + n 468 + " (expected " + len + ")"); 469 470 // Check that source didn't change, and was read correctly 471 src.verify(); 472 473 // Check that target was updated correctly 474 if (fc.position() != pos) 475 throw new Failure("Position changed"); 476 if (debug) 477 dump(fc); 478 checkRandomBytes(fc, off, len, seed); 479 writeZeroBytes(fc, off, len); 480 checkZeroBytes(fc, 0, MAX_FILE_SIZE); 481 } 482 main(String[] args)483 public static void main(String[] args) 484 throws Exception 485 { 486 if (args.length > 0) { 487 if (args[0].indexOf('v') >= 0) 488 verbose = true; 489 if (args[0].indexOf('d') >= 0) 490 debug = verbose = true; 491 } 492 493 File testDir = new File(System.getProperty("test.dir", ".")); 494 495 sourceFile = File.createTempFile("xfer.src.", "", testDir); 496 sourceFile.deleteOnExit(); 497 targetFile = File.createTempFile("xfer.tgt.", "", testDir); 498 targetFile.deleteOnExit(); 499 500 File fn = File.createTempFile("xfer.fch.", "", testDir); 501 fn.deleteOnExit(); 502 503 Random rnd = new Random(); 504 int failures = 0; 505 506 try (FileChannel fc = new RandomAccessFile(fn, "rw").getChannel()) { 507 for (boolean to = false;; to = true) { 508 for (boolean user = false;; user = true) { 509 if (!verbose) 510 out.print((to ? "To " : "From ") + 511 (user ? "user channel" : "file channel") 512 + ":"); 513 IntGenerator offGen = new IntGenerator(MAX_XFER_SIZE + 2); 514 while (offGen.hasNext()) { 515 int off = offGen.next(); 516 if (!verbose) out.print(" " + off); 517 IntGenerator lenGen = new IntGenerator(MAX_XFER_SIZE + 2); 518 while (lenGen.hasNext()) { 519 int len = lenGen.next(); 520 long s = rnd.nextLong(); 521 String chName = null; 522 try { 523 if (to) { 524 Target tgt; 525 if (user) 526 tgt = new UserTarget(len, s); 527 else 528 tgt = new FileTarget(len, s); 529 chName = tgt.name(); 530 testTo(s, fc, off, len, tgt); 531 } 532 else { 533 Source src; 534 if (user) 535 src = new UserSource(len, s); 536 else 537 src = new FileSource(len, s); 538 chName = src.name(); 539 testFrom(s, src, fc, off, len); 540 } 541 } catch (Failure x) { 542 out.println(); 543 out.println("FAILURE: " + chName 544 + ", offset " + off 545 + ", length " + len); 546 x.printStackTrace(out); 547 failures++; 548 } 549 } 550 } 551 if (!verbose) 552 out.println(); 553 if (user) 554 break; 555 } 556 if (to) 557 break; 558 } 559 } 560 561 sourceFile.delete(); 562 targetFile.delete(); 563 fn.delete(); 564 565 if (failures > 0) { 566 out.println(); 567 throw new RuntimeException("Some tests failed"); 568 } 569 570 out.println("Test succeeded."); 571 } 572 } 573