0N/A/*
3261N/A * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/* @test
0N/A * @summary Comprehensive test for FileChannel.transfer{From,To}
0N/A * @bug 4708120
0N/A * @author Mark Reinhold
0N/A */
0N/A
0N/Aimport java.io.*;
0N/Aimport java.nio.*;
0N/Aimport java.nio.channels.*;
0N/Aimport java.util.*;
0N/A
0N/A
0N/Apublic class Transfers {
0N/A
0N/A static PrintStream out = System.out;
0N/A
0N/A private static class Failure
0N/A extends RuntimeException
0N/A {
0N/A
0N/A Failure(Exception x) {
0N/A super(x);
0N/A }
0N/A
0N/A Failure(String s) {
0N/A super(s);
0N/A }
0N/A
0N/A }
0N/A
0N/A
0N/A // -- Writing and reading random bytes --
0N/A
0N/A private static void writeBytes(byte[] ba, FileChannel fc,
0N/A int off, int len)
0N/A throws IOException
0N/A {
0N/A fc.position(off);
0N/A if (fc.write(ByteBuffer.wrap(ba, 0, len)) != len)
0N/A throw new IOException("Incomplete write");
0N/A fc.position(0);
0N/A }
0N/A
0N/A private static void writeRandomBytes(long seed,
0N/A FileChannel fc, int off, int len)
0N/A throws IOException
0N/A {
0N/A Random r = new Random(seed);
0N/A byte[] ba = new byte[len];
0N/A r.nextBytes(ba);
0N/A writeBytes(ba, fc, off, len);
0N/A }
0N/A
0N/A private static void writeZeroBytes(FileChannel fc, int off, int len)
0N/A throws IOException
0N/A {
0N/A byte[] ba = new byte[len];
0N/A writeBytes(ba, fc, off, len);
0N/A }
0N/A
0N/A private static void checkBytes(FileChannel fc, int off, int len,
0N/A byte[] bytes)
0N/A throws IOException
0N/A {
0N/A ByteBuffer bb = ByteBuffer.allocate(len);
0N/A fc.position(off);
0N/A if (fc.read(bb) != len)
0N/A throw new IOException("Incomplete read");
0N/A bb.flip();
0N/A ByteBuffer bab = ByteBuffer.wrap(bytes, 0, len);
0N/A if (!bb.equals(bab))
0N/A throw new Failure("Wrong data written");
0N/A }
0N/A
0N/A private static void checkRandomBytes(FileChannel fc, int off, int len,
0N/A long seed)
0N/A throws IOException
0N/A {
0N/A byte[] ba = new byte[len];
0N/A Random r = new Random(seed);
0N/A r.nextBytes(ba);
0N/A checkBytes(fc, off, len, ba);
0N/A }
0N/A
0N/A private static void checkZeroBytes(FileChannel fc, int off, int len)
0N/A throws IOException
0N/A {
0N/A byte[] ba = new byte[len];
0N/A checkBytes(fc, off, len, ba);
0N/A }
0N/A
0N/A // For debugging
0N/A //
0N/A private static void dump(FileChannel fc)
0N/A throws IOException
0N/A {
0N/A int sz = (int)fc.size();
0N/A ByteBuffer bb = ByteBuffer.allocate(sz);
0N/A fc.position(0);
0N/A if (fc.read(bb) != sz)
0N/A throw new IOException("Incomplete read");
0N/A bb.flip();
0N/A byte prev = -1;
0N/A int r = 0; // Repeats
0N/A int n = 0;
0N/A while (bb.hasRemaining() && (n < 32)) {
0N/A byte b = bb.get();
0N/A if (b == prev) {
0N/A r++;
0N/A continue;
0N/A }
0N/A if (r > 0) {
0N/A int c = prev & 0xff;
0N/A if (c < 0x10)
0N/A out.print('0');
0N/A out.print(Integer.toHexString(c));
0N/A if (r > 1) {
0N/A out.print("[");
0N/A out.print(r);
0N/A out.print("]");
0N/A }
0N/A n++;
0N/A }
0N/A prev = b;
0N/A r = 1;
0N/A }
0N/A if (r > 0) {
0N/A int c = prev & 0xff;
0N/A if (c < 0x10)
0N/A out.print('0');
0N/A out.print(Integer.toHexString(c));
0N/A if (r > 1) {
0N/A out.print("[");
0N/A out.print(r);
0N/A out.print("]");
0N/A }
0N/A n++;
0N/A }
0N/A if (bb.hasRemaining())
0N/A out.print("...");
0N/A out.println();
0N/A }
0N/A
0N/A
0N/A
0N/A static File sourceFile;
0N/A static File targetFile;
0N/A
0N/A // -- Self-verifying sources and targets --
0N/A
0N/A static abstract class Source {
0N/A
0N/A protected final int size;
0N/A protected final long seed;
0N/A private final String name;
0N/A
0N/A Source(int size, long seed, String name) {
0N/A this.size = size;
0N/A this.seed = seed;
0N/A this.name = name;
0N/A }
0N/A
0N/A String name() {
0N/A return name;
0N/A }
0N/A
0N/A abstract ReadableByteChannel channel();
0N/A
0N/A abstract void verify() throws IOException;
0N/A
0N/A }
0N/A
0N/A static class FileSource
0N/A extends Source
0N/A {
0N/A private final File fn;
0N/A private final RandomAccessFile raf;
0N/A private final FileChannel fc;
0N/A
0N/A FileSource(int size, long seed) throws IOException {
0N/A super(size, seed, "FileChannel");
0N/A fn = sourceFile;
0N/A raf = new RandomAccessFile(fn, "rw");
0N/A fc = raf.getChannel();
0N/A fc.position(0);
0N/A writeRandomBytes(seed, fc, 0, size);
0N/A }
0N/A
0N/A ReadableByteChannel channel() {
0N/A return fc;
0N/A }
0N/A
0N/A void verify() throws IOException {
0N/A if (fc.position() != size)
0N/A throw new Failure("Wrong position: "
0N/A + fc.position() + " (expected " + size +
0N/A ")");
0N/A checkRandomBytes(fc, 0, size, seed);
0N/A fc.close();
0N/A raf.close(); // Bug in 1.4.0
0N/A }
0N/A
0N/A }
0N/A
0N/A static class UserSource
0N/A extends Source
0N/A {
0N/A private ReadableByteChannel ch;
0N/A private final ByteBuffer src;
0N/A
0N/A UserSource(int size, long seed) {
0N/A super(size, seed, "UserChannel");
0N/A
0N/A final byte[] bytes = new byte[size + 1];
0N/A Random r = new Random(seed);
0N/A r.nextBytes(bytes);
0N/A src = ByteBuffer.wrap(bytes);
0N/A
0N/A ch = new ReadableByteChannel() {
0N/A public int read(ByteBuffer dst) {
0N/A if (!src.hasRemaining())
0N/A return -1;
0N/A int nr = Math.min(src.remaining(), dst.remaining());
0N/A ByteBuffer s = src.duplicate();
0N/A s.limit(s.position() + nr);
0N/A dst.put(s);
0N/A src.position(src.position() + nr);
0N/A return nr;
0N/A }
0N/A public boolean isOpen() {
0N/A return true;
0N/A }
0N/A public void close() { }
0N/A };
0N/A }
0N/A
0N/A ReadableByteChannel channel() {
0N/A return ch;
0N/A }
0N/A
0N/A void verify() {
0N/A if (src.remaining() != 1)
0N/A throw new Failure("Source has " + src.remaining()
0N/A + " bytes remaining (expected 1)");
0N/A }
0N/A
0N/A }
0N/A
0N/A static abstract class Target {
0N/A
0N/A protected final int size;
0N/A protected final long seed;
0N/A private final String name;
0N/A
0N/A Target(int size, long seed, String name) {
0N/A this.size = size;
0N/A this.seed = seed;
0N/A this.name = name;
0N/A }
0N/A
0N/A String name() {
0N/A return name;
0N/A }
0N/A
0N/A abstract WritableByteChannel channel();
0N/A
0N/A abstract void verify() throws IOException;
0N/A
0N/A }
0N/A
0N/A static class FileTarget
0N/A extends Target
0N/A {
0N/A private final File fn;
0N/A private final RandomAccessFile raf;
0N/A private final FileChannel fc;
0N/A
0N/A FileTarget(int size, long seed) throws IOException {
0N/A super(size, seed, "FileChannel");
0N/A fn = targetFile;
0N/A raf = new RandomAccessFile(fn, "rw");
0N/A fc = raf.getChannel();
0N/A fc.position(0);
0N/A }
0N/A
0N/A WritableByteChannel channel() {
0N/A return fc;
0N/A }
0N/A
0N/A void verify() throws IOException {
0N/A if (fc.position() != size)
0N/A throw new Failure("Wrong position: "
0N/A + fc.position() + " (expected " + size + ")");
0N/A checkRandomBytes(fc, 0, size, seed);
0N/A fc.close();
0N/A raf.close(); // Bug in 1.4.0
0N/A }
0N/A
0N/A }
0N/A
0N/A static class UserTarget
0N/A extends Target
0N/A {
0N/A private WritableByteChannel ch;
0N/A private final ByteBuffer dst;
0N/A
0N/A UserTarget(int size, long seed) {
0N/A super(size, seed, "UserChannel");
0N/A dst = ByteBuffer.wrap(new byte[size + 1]);
0N/A
0N/A ch = new WritableByteChannel() {
0N/A public int write(ByteBuffer src) {
0N/A int nr = Math.min(src.remaining(), dst.remaining());
0N/A ByteBuffer s = src.duplicate();
0N/A s.limit(s.position() + nr);
0N/A dst.put(s);
0N/A src.position(src.position() + nr);
0N/A return nr;
0N/A }
0N/A public boolean isOpen() {
0N/A return true;
0N/A }
0N/A public void close() { }
0N/A };
0N/A }
0N/A
0N/A WritableByteChannel channel() {
0N/A return ch;
0N/A }
0N/A
0N/A void verify() {
0N/A if (dst.remaining() != 1)
0N/A throw new Failure("Destination has " + dst.remaining()
0N/A + " bytes remaining (expected 1)");
0N/A byte[] ba = new byte[size];
0N/A Random r = new Random(seed);
0N/A r.nextBytes(ba);
0N/A dst.flip();
0N/A ByteBuffer rbb = ByteBuffer.wrap(ba, 0, size);
0N/A if (!dst.equals(rbb))
0N/A throw new Failure("Wrong data written");
0N/A }
0N/A
0N/A }
0N/A
0N/A
0N/A // Generates a sequence of ints of the form 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0N/A // 15, 16, 17, 31, 32, 33, ..., 2^i-1, 2^i, 2^i+1, ..., max.
0N/A
0N/A static class IntGenerator {
0N/A
0N/A private int max;
0N/A private int cur = -1;
0N/A private int p2 = 8;
0N/A
0N/A IntGenerator(int max) {
0N/A this.max = max;
0N/A }
0N/A
0N/A boolean hasNext() {
0N/A return cur < max;
0N/A }
0N/A
0N/A int next() {
0N/A if (cur >= max)
0N/A throw new IllegalStateException();
0N/A if (cur < 6) {
0N/A cur++;
0N/A return cur;
0N/A }
0N/A if (cur == p2 + 1) {
0N/A p2 <<= 1;
0N/A cur = p2 - 1;
0N/A return cur;
0N/A }
0N/A cur++;
0N/A return cur;
0N/A }
0N/A
0N/A }
0N/A
0N/A
0N/A // -- Tests --
0N/A
0N/A private static final int MAX_XFER_SIZE = 1 << 14;
0N/A private static final int MAX_FILE_SIZE = MAX_XFER_SIZE << 1;
0N/A
0N/A private static boolean debug = false;
0N/A private static boolean verbose = false;
0N/A
0N/A static void show(String dir, String channelName, int off, int len) {
0N/A if (!verbose)
0N/A return;
0N/A out.println(dir + " " + channelName +
0N/A ": offset " + off + ", length " + len);
0N/A }
0N/A
0N/A static void testTo(long seed, FileChannel fc, int off, int len, Target tgt)
0N/A throws IOException
0N/A {
0N/A show("To", tgt.name(), off, len);
0N/A
0N/A // Clear source, then randomize just the source region
0N/A writeZeroBytes(fc, 0, MAX_FILE_SIZE);
0N/A writeRandomBytes(seed, fc, off, len);
0N/A
0N/A // Randomize position
0N/A int pos = (int)seed & 0xfff;
0N/A fc.position(pos);
0N/A
0N/A int n = (int)fc.transferTo(off, len, tgt.channel());
0N/A if (n != len)
0N/A throw new Failure("Incorrect transfer length: " + n
0N/A + " (expected " + len + ")");
0N/A
0N/A // Check that source wasn't changed
0N/A if (fc.position() != pos)
0N/A throw new Failure("Position changed");
0N/A if (debug)
0N/A dump(fc);
0N/A checkRandomBytes(fc, off, len, seed);
0N/A writeZeroBytes(fc, off, len);
0N/A checkZeroBytes(fc, 0, MAX_FILE_SIZE);
0N/A
0N/A // Check that target was updated correctly
0N/A tgt.verify();
0N/A }
0N/A
0N/A static void testFrom(long seed, Source src, FileChannel fc, int off, int len)
0N/A throws IOException
0N/A {
0N/A show("From", src.name(), off, len);
0N/A
0N/A // Clear target
0N/A writeZeroBytes(fc, 0, MAX_FILE_SIZE);
0N/A
0N/A // Randomize position
0N/A int pos = (int)seed & 0xfff;
0N/A fc.position(pos);
0N/A
0N/A int n = (int)fc.transferFrom(src.channel(), off, len);
0N/A if (n != len)
0N/A throw new Failure("Incorrect transfer length: " + n
0N/A + " (expected " + len + ")");
0N/A
0N/A // Check that source didn't change, and was read correctly
0N/A src.verify();
0N/A
0N/A // Check that target was updated correctly
0N/A if (fc.position() != pos)
0N/A throw new Failure("Position changed");
0N/A if (debug)
0N/A dump(fc);
0N/A checkRandomBytes(fc, off, len, seed);
0N/A writeZeroBytes(fc, off, len);
0N/A checkZeroBytes(fc, 0, MAX_FILE_SIZE);
0N/A }
0N/A
0N/A public static void main(String[] args)
0N/A throws Exception
0N/A {
0N/A if (args.length > 0) {
0N/A if (args[0].indexOf('v') >= 0)
0N/A verbose = true;
0N/A if (args[0].indexOf('d') >= 0)
0N/A debug = verbose = true;
0N/A }
0N/A
0N/A sourceFile = File.createTempFile("xfer.src.", "");
0N/A sourceFile.deleteOnExit();
0N/A targetFile = File.createTempFile("xfer.tgt.", "");
0N/A targetFile.deleteOnExit();
0N/A
0N/A File fn = File.createTempFile("xfer.fch.", "");
0N/A fn.deleteOnExit();
0N/A FileChannel fc = new RandomAccessFile(fn, "rw").getChannel();
0N/A
0N/A Random rnd = new Random();
0N/A int failures = 0;
0N/A
0N/A for (boolean to = false;; to = true) {
0N/A for (boolean user = false;; user = true) {
0N/A if (!verbose)
0N/A out.print((to ? "To " : "From ") +
0N/A (user ? "user channel" : "file channel")
0N/A + ":");
0N/A IntGenerator offGen = new IntGenerator(MAX_XFER_SIZE + 2);
0N/A while (offGen.hasNext()) {
0N/A int off = offGen.next();
0N/A if (!verbose) out.print(" " + off);
0N/A IntGenerator lenGen = new IntGenerator(MAX_XFER_SIZE + 2);
0N/A while (lenGen.hasNext()) {
0N/A int len = lenGen.next();
0N/A long s = rnd.nextLong();
0N/A String chName = null;
0N/A try {
0N/A if (to) {
0N/A Target tgt;
0N/A if (user)
0N/A tgt = new UserTarget(len, s);
0N/A else
0N/A tgt = new FileTarget(len, s);
0N/A chName = tgt.name();
0N/A testTo(s, fc, off, len, tgt);
0N/A }
0N/A else {
0N/A Source src;
0N/A if (user)
0N/A src = new UserSource(len, s);
0N/A else
0N/A src = new FileSource(len, s);
0N/A chName = src.name();
0N/A testFrom(s, src, fc, off, len);
0N/A }
0N/A } catch (Failure x) {
0N/A out.println();
0N/A out.println("FAILURE: " + chName
0N/A + ", offset " + off
0N/A + ", length " + len);
0N/A x.printStackTrace(out);
0N/A failures++;
0N/A }
0N/A }
0N/A }
0N/A if (!verbose)
0N/A out.println();
0N/A if (user)
0N/A break;
0N/A }
0N/A if (to)
0N/A break;
0N/A }
0N/A
2546N/A sourceFile.delete();
2546N/A targetFile.delete();
2546N/A fn.delete();
2546N/A
0N/A if (failures > 0) {
0N/A out.println();
0N/A throw new RuntimeException("Some tests failed");
0N/A }
0N/A
0N/A }
0N/A
0N/A}