com.tinspx.util.io.ByteUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.tinspx.util.io.ByteUtils.java

Source

/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.tinspx.util.io;

import com.google.common.base.Function;
import static com.google.common.base.Preconditions.*;
import com.google.common.base.Supplier;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.PrimitiveSink;
import com.google.common.io.ByteProcessor;
import com.google.common.io.ByteSink;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.ListenableFuture;
import com.tinspx.util.io.charset.CharsetDetector;
import java.io.Closeable;
import java.io.DataOutput;
import java.io.File;
import java.io.FileInputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.Arrays;
import javax.annotation.Nullable;
import javax.annotation.WillNotClose;
import lombok.experimental.Delegate;
import lombok.NonNull;
import lombok.Synchronized;

/**
 * {@link ByteSource}, {@link ByteSink}, {@link ByteBuffer},
 * {@link InputStream}, {@link OutputStream}, {@link HashFunction},
 * {@link PrimitiveSink}, {@link ByteProcessor}, {@link ReadableByteChannel},
 * {@link WritableByteChannel}, and {@code byte} array utility methods.
 * 
 * @author Ian
 */
public class ByteUtils {

    /**
     * indicates that a method uses a thread local array
     */
    @Retention(RetentionPolicy.SOURCE)
    @Target(ElementType.METHOD)
    static @interface ThreadLocalArray {
        /**
         * the length of the thread local array being used
         */
        int value() default 0;
    }

    public static final byte EMPTY_ARRAY[] = new byte[0];

    public static byte[] emptyArray() {
        return EMPTY_ARRAY;
    }

    public static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(EMPTY_ARRAY);

    public static ByteBuffer emptyBuffer() {
        return EMPTY_BUFFER;
    }

    private static final ThreadLocal<byte[]> BYTE_ARRAY_8K = new ThreadLocal<byte[]>() {
        @Override
        protected byte[] initialValue() {
            return new byte[1024 * 8];
        }
    };

    private static final ThreadLocal<ByteBuffer> BYTE_BUFFER_8K = new ThreadLocal<ByteBuffer>() {
        @Override
        protected ByteBuffer initialValue() {
            return ByteBuffer.wrap(threadLocalArray8K());
        }
    };

    /**
     * Returns a thread local byte array with 8k bytes. There are no guarantees
     * about the contents of the returned array, and it should be assumed
     * that there is random data in the array.
     * <p>
     * Care must be used when using the returned array as it is thread local.
     * The calling method must make sure to never relinquish control to any
     * code that may also use this array.
     * 
     * @return a thread local byte array with a length of 8192
     */
    static byte[] threadLocalArray8K() {
        return BYTE_ARRAY_8K.get();
    }

    /**
     * Returns a cleared thread local ByteBuffer with a capacity of 8192. The
     * backing byte array is the same array returned from
     * {@link #threadLocalArray8K()}.
     */
    static ByteBuffer threadLocalBuffer8K() {
        final ByteBuffer buf = BYTE_BUFFER_8K.get();
        buf.clear();
        return buf;
    }

    public static InputStream emptyInputStream() {
        return EmptyInputStream.INSTANCE;
    }

    public static ReadableByteChannel emptyReadableByteChannel() {
        return EmptyInputStream.INSTANCE;
    }

    private static final class EmptyInputStream extends InputStream implements ReadableByteChannel {

        private static final EmptyInputStream INSTANCE = new EmptyInputStream();

        @Override
        public int read() throws IOException {
            return -1;
        }

        @Override
        public int read(ByteBuffer dst) throws IOException {
            checkNotNull(dst);
            return -1;
        }

        @Override
        public boolean isOpen() {
            return true;
        }

        @Override
        public boolean markSupported() {
            return true;
        }

        @Override
        public void reset() throws IOException {
        }

        @Override
        public void mark(int readlimit) {
            checkArgument(readlimit >= 0, "readlimit (%s) may not be negative", readlimit);
        }

        @Override
        public long skip(long n) throws IOException {
            return 0;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            checkPositionIndexes(off, off + len, b.length);
            return -1;
        }
    }

    public static boolean rangeEquals(byte[] a, int aoff, byte[] b, int boff, int len) {
        return rangeEquals(a, aoff, len, b, boff, len);
    }

    public static boolean rangeEquals(byte[] a, int aoff, int alen, byte[] b, int boff, int blen) {
        checkPositionIndexes(aoff, aoff + alen, a.length);
        checkPositionIndexes(boff, boff + blen, b.length);
        if (alen != blen) {
            return false;
        }
        if (a == b && aoff == boff) {
            //same array, same offset, and same length
            return true;
        }
        for (int i = 0; i < alen; i++) {
            if (a[aoff + i] != b[boff + i]) {
                return false;
            }
        }
        return true;
    }

    static int checkByteSourceSize(ByteSource source) throws IOException {
        final long size = source.size();
        if (size > Integer.MAX_VALUE) {
            throw new OutOfMemoryError(String.format("%s is too large (%d) for byte array", source, size));
        }
        return (int) size;
    }

    /**
     * Provides an alternative to {@link ByteSource#read()}.
     */
    @ThreadLocalArray(8192)
    public static byte[] toByteArray(ByteSource source) throws IOException {
        final Closer closer = Closer.create();
        try {
            if (source instanceof ChannelSource && ((ChannelSource) source).hasKnownSize()) {
                return toByteArray(closer.register(source.openStream()), checkByteSourceSize(source));
            } else {
                return toByteArray(closer.register(source.openStream()));
            }
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Used instead of {@link #toByteArray(ByteSource)} when the size of the
     * {@code ByteSource} is known and {@link ByteSource#size() size()} should
     * not be called on {@code source}.
     */
    @ThreadLocalArray(8192)
    public static byte[] toByteArray(ByteSource source, int expectedSize) throws IOException {
        final Closer closer = Closer.create();
        try {
            return toByteArray(closer.register(source.openStream()), expectedSize);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Reads {@code a} into a {@code byte} array.
     */
    @ThreadLocalArray(8192)
    public static byte[] toByteArray(@NonNull @WillNotClose InputStream in) throws IOException {
        final byte local[] = threadLocalArray8K();
        BAOutputStream out = new BAOutputStream(local);
        out.write(in);
        return out.buf == local ? out.toByteArray() : out.backingToByteArray();
    }

    @ThreadLocalArray(8192)
    public static byte[] toByteArray(@NonNull File file) throws IOException {
        final int size = Ints.checkedCast(file.length());
        Closer closer = Closer.create();
        try {
            return toByteArray(closer.register(new FileInputStream(file)), size);
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
    }

    /**
     * Reads {@code in} fully into a {@code byte} array with an expected size of
     * {@code expectedSize}.
     */
    @ThreadLocalArray(8192)
    public static byte[] toByteArray(@WillNotClose InputStream in, final int expectedSize) throws IOException {
        if (expectedSize <= 0) {
            return toByteArray(in);
        }

        byte[] expected = new byte[expectedSize];
        int r = ByteStreams.read(in, expected, 0, expectedSize);
        if (r < expectedSize) {
            return Arrays.copyOf(expected, r);
        }
        assert r == expectedSize;

        r = in.read();
        if (r < 0) {
            return expected;
        }
        BAOutputStream out = new BAOutputStream(threadLocalArray8K());
        out.write(r);
        out.write(in);

        byte[] result = new byte[expectedSize + out.size()];
        System.arraycopy(expected, 0, result, 0, expectedSize);
        out.writeTo(result, expectedSize, out.size());
        return result;
    }

    /**
     * Reads {@code channel} into a {@code byte} array.
     */
    @ThreadLocalArray(8192)
    public static byte[] toByteArray(@NonNull @WillNotClose ReadableByteChannel channel) throws IOException {
        final byte local[] = threadLocalArray8K();
        BAOutputStream out = new BAOutputStream(local);
        out.write(channel);
        return out.buf == local ? out.toByteArray() : out.backingToByteArray();
    }

    @ThreadLocalArray(8192)
    public static byte[] toByteArray(@NonNull @WillNotClose ReadableByteChannel channel, int expectedSize)
            throws IOException {
        if (expectedSize <= 0) {
            return toByteArray(channel);
        }
        return toByteArray(asInputStream(channel), expectedSize);
    }

    static void checkLimit(int limit) {
        if (limit < 0) {
            throw new IllegalArgumentException(String.format("limit (%d) cannot be negative", limit));
        }
    }

    static void checkLimit(long limit) {
        if (limit < 0) {
            throw new IllegalArgumentException(String.format("limit (%d) cannot be negative", limit));
        }
    }

    /**
     * {@link #copy(InputStream, OutputStream, long)} with a {@code limit} of
     * {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose InputStream from, @WillNotClose OutputStream to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. Neither {@code from} nor
     * {@code to} is closed, and {@code to} is not flushed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose InputStream from, @NonNull @WillNotClose OutputStream to,
            long limit) throws IOException {
        return copy(from, to, limit, threadLocalArray8K());
    }

    public static long copy(@NonNull @WillNotClose InputStream from, @NonNull @WillNotClose OutputStream to,
            long limit, @NonNull byte[] buffer) throws IOException {
        checkLimit(limit);
        long total = 0;
        while (total < limit) {
            int r = from.read(buffer, 0, (int) Math.min(buffer.length, limit - total));
            if (r > 0) {
                to.write(buffer, 0, r);
                total += r;
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
     * {@link #copy(ReadableByteChannel, OutputStream, long)} with a
     * {@code limit} of {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose ReadableByteChannel from, @WillNotClose OutputStream to)
            throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. Neither {@code from} nor
     * {@code to} is closed, and {@code to} is not flushed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose ReadableByteChannel from, @NonNull @WillNotClose OutputStream to,
            long limit) throws IOException {
        checkLimit(limit);
        final byte[] buf = threadLocalArray8K();
        final ByteBuffer bb = ByteBuffer.wrap(buf);
        long total = 0;
        while (total < limit) {
            bb.limit((int) Math.min(buf.length, limit - total));
            int r = from.read(bb);
            if (r > 0) {
                to.write(buf, 0, r);
                total += r;
                bb.clear();
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
     * {@link #copy(InputStream, WritableByteChannel, long)} with a
     * {@code limit} of {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose InputStream from, @WillNotClose WritableByteChannel to)
            throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. Neither {@code from} nor
     * {@code to} is closed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose InputStream from, @NonNull @WillNotClose WritableByteChannel to,
            long limit) throws IOException {
        checkLimit(limit);
        final byte[] buf = threadLocalArray8K();
        final ByteBuffer bb = ByteBuffer.wrap(buf);
        long total = 0;
        while (total < limit) {
            int r = from.read(buf, 0, (int) Math.min(buf.length, limit - total));
            if (r > 0) {
                bb.limit(r);
                do {
                    to.write(bb);
                } while (bb.hasRemaining());
                total += r;
                bb.clear();
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
     * {@link #copy(ReadableByteChannel, WritableByteChannel, long)} with a
     * {@code limit} of {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose ReadableByteChannel from, @WillNotClose WritableByteChannel to)
            throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. Neither {@code from} nor
     * {@code to} is closed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose ReadableByteChannel from,
            @NonNull @WillNotClose WritableByteChannel to, long limit) throws IOException {
        checkLimit(limit);
        final ByteBuffer bb = threadLocalBuffer8K();
        long total = 0;
        while (total < limit) {
            bb.limit((int) Math.min(bb.capacity(), limit - total));
            int r = from.read(bb);
            if (r > 0) {
                bb.flip();
                do {
                    to.write(bb);
                } while (bb.hasRemaining());
                total += r;
                bb.clear();
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
     * {@link #copy(ByteSource, ByteSink, long)} with a {@code limit} of
     * {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(ByteSource from, ByteSink to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. This method <i>always</i>
     * copies {@code from} into {@code to} in chunks;
     * {@link ByteSource#copyTo(ByteSink)} or
     * {@link ByteSource#copyTo(OutputStream)} is never used.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull ByteSource from, @NonNull ByteSink to, long limit) throws IOException {
        checkLimit(limit);
        final Closer closer = Closer.create();
        try {
            OutputStream out = closer.register(to.openStream());
            long total = copy(closer.register(from.openStream()), out, limit);
            out.flush();
            return total;
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * {@link #copy(InputStream, ByteSink, long)} with a {@code limit} of
     * {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose InputStream from, ByteSink to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code from} is not closed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose InputStream from, @NonNull ByteSink to, long limit)
            throws IOException {
        checkLimit(limit);
        final Closer closer = Closer.create();
        try {
            OutputStream out = closer.register(to.openStream());
            long total = copy(from, out, limit);
            out.flush();
            return total;
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * {@link #copy(ReadableByteChannel, ByteSink, long)} with a {@code limit}
     * of {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose ReadableByteChannel from, ByteSink to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code from} is not closed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose ReadableByteChannel from, @NonNull ByteSink to, long limit)
            throws IOException {
        checkLimit(limit);
        final Closer closer = Closer.create();
        try {
            if (to instanceof ChannelSink) {
                return copy(from, closer.register(((ChannelSink) to).openChannel()), limit);
            } else {
                OutputStream out = closer.register(to.openStream());
                long total = copy(from, out, limit);
                out.flush();
                return total;
            }
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * {@link #copy(ByteSource, OutputStream, long)} with a {@code limit} of
     * {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(ByteSource from, @WillNotClose OutputStream to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code to} is not closed or
     * flushed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull ByteSource from, @NonNull @WillNotClose OutputStream to, long limit)
            throws IOException {
        final Closer closer = Closer.create();
        try {
            return copy(closer.register(from.openStream()), to, limit);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * {@link #copy(ByteSource, WritableByteChannel, long)} with a
     * {@code limit} of {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(ByteSource from, @WillNotClose WritableByteChannel to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code to} is not closed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull ByteSource from, @NonNull @WillNotClose WritableByteChannel to, long limit)
            throws IOException {
        checkLimit(limit);
        final Closer closer = Closer.create();
        try {
            if (from instanceof ChannelSource) {
                return copy(closer.register(((ChannelSource) from).openChannel()), to, limit);
            } else {
                return copy(closer.register(from.openStream()), to, limit);
            }
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Copies as many bytes as possible from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code from} is not closed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    public static int copy(@NonNull @WillNotClose ReadableByteChannel from, @NonNull ByteBuffer to)
            throws IOException {
        int total = 0;
        while (to.hasRemaining()) {
            int r = from.read(to);
            if (r > 0) {
                total += r;
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
     * Copies as many bytes as possible from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code from} is not closed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    @ThreadLocalArray(8192)
    public static int copy(@NonNull @WillNotClose InputStream from, @NonNull ByteBuffer to) throws IOException {
        if (from instanceof ReadableByteChannel) {
            return copy((ReadableByteChannel) from, to);
        }
        if (to.hasArray()) {
            final byte[] bytes = to.array();
            final int off = to.arrayOffset() + to.position();
            int len = to.remaining(), total = 0;
            while (len > 0) {
                int r = from.read(bytes, off + total, len);
                if (r > 0) {
                    total += r;
                    len -= r;
                } else if (r < 0) {
                    break;
                }
            }
            to.position(to.position() + total);
            return total;
        }
        final byte[] bytes = threadLocalArray8K();
        int total = 0;
        while (to.hasRemaining()) {
            int r = from.read(bytes, 0, Math.min(to.remaining(), bytes.length));
            if (r > 0) {
                total += r;
                to.put(bytes, 0, r);
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
     * Copies the entire contents of {@code from} into {@code to}. {@code to} is
     * not closed.
     *
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    public static void copy(@NonNull ByteBuffer from, @NonNull @WillNotClose WritableByteChannel to)
            throws IOException {
        while (from.hasRemaining()) {
            to.write(from);
        }
    }

    /**
     * Copies the entire contents of {@code from} into {@code to}. {@code to} is
     * not closed or flushed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    @ThreadLocalArray(8192)
    public static void copy(@NonNull ByteBuffer from, @NonNull @WillNotClose OutputStream to) throws IOException {
        if (to instanceof WritableByteChannel) {
            copy(from, (WritableByteChannel) to);
            return;
        }
        if (!from.hasRemaining()) {
            return;
        }
        if (from.hasArray()) {
            to.write(from.array(), from.arrayOffset() + from.position(), from.remaining());
            from.position(from.limit());
            return;
        }
        final byte bytes[] = threadLocalArray8K();
        do {
            int r = Math.min(bytes.length, from.remaining());
            from.get(bytes, 0, r);
            to.write(bytes, 0, r);
        } while (from.hasRemaining());
    }

    /**
     * Copies the entire contents of {@code from} into {@code to}.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    @ThreadLocalArray(8192)
    public static void copy(@NonNull ByteBuffer from, @NonNull PrimitiveSink to) {
        if (!from.hasRemaining()) {
            return;
        }
        if (from.hasArray()) {
            to.putBytes(from.array(), from.arrayOffset() + from.position(), from.remaining());
            from.position(from.limit());
            return;
        }
        final byte bytes[] = threadLocalArray8K();
        do {
            int r = Math.min(bytes.length, from.remaining());
            from.get(bytes, 0, r);
            to.putBytes(bytes, 0, r);
        } while (from.hasRemaining());
    }

    /**
     * {@link #copy(InputStream, PrimitiveSink, long)} with a {@code limit} of
     * {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose InputStream from, PrimitiveSink to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. Neither {@code from} nor
     * {@code to} is closed, and {@code to} is not flushed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose InputStream from, @NonNull PrimitiveSink to, long limit)
            throws IOException {
        checkLimit(limit);
        final byte[] buf = threadLocalArray8K();
        long total = 0;
        while (total < limit) {
            int r = from.read(buf, 0, (int) Math.min(buf.length, limit - total));
            if (r > 0) {
                to.putBytes(buf, 0, r);
                total += r;
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
    * {@link #copy(ByteSource, PrimitiveSink, long)} with a {@code limit} of
    * {@link Long#MAX_VALUE}
    */
    @ThreadLocalArray(8192)
    public static long copy(ByteSource from, PrimitiveSink to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code to} is not closed or
     * flushed.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull ByteSource from, @NonNull PrimitiveSink to, long limit) throws IOException {
        checkLimit(limit);
        final Closer closer = Closer.create();
        try {
            return copy(closer.register(from.openStream()), to, limit);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Copies the as many bytes as possible from {@code from} into {@code to}.
     * Byte processing ends when all bytes have been processed or when
     * {@link ByteProcessor#processBytes(byte[], int, int) processBytes} returns
     * {@code false}.
     * 
     * @param from the source to read bytes from
     * @param to the ByteProcessor to process the bytes read from {@code from}
     * @return the last result of calling
     * {@link ByteProcessor#processBytes(byte[], int, int) processBytes},
     * indicating if byte processing should continue
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    @ThreadLocalArray(8192)
    public static boolean copy(@NonNull ByteBuffer from, @NonNull ByteProcessor<?> to) throws IOException {
        if (!from.hasRemaining()) {
            return true;
        }
        if (from.hasArray()) {
            boolean p = to.processBytes(from.array(), from.arrayOffset() + from.position(), from.remaining());
            from.position(from.limit());
            return p;
        }
        final byte[] bytes = threadLocalArray8K();
        int r;
        boolean p;
        do {
            r = Math.min(from.remaining(), bytes.length);
            from.get(bytes, 0, r);
        } while ((p = to.processBytes(bytes, 0, r)) && from.hasRemaining());
        return p;
    }

    /**
     * {@link #copy(InputStream, ByteProcessor, long)} with a
     * {@code limit} of {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(@WillNotClose InputStream from, ByteProcessor<?> to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. {@code from} is not closed.
     * Byte processing stops on EOF or when
     * {@link ByteProcessor#processBytes(byte[], int, int) processBytes} returns
     * {@code false}.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull @WillNotClose InputStream from, @NonNull ByteProcessor<?> to, long limit)
            throws IOException {
        checkLimit(limit);
        final byte[] buf = threadLocalArray8K();
        long total = 0;
        boolean proc = true;
        while (total < limit && proc) {
            int r = from.read(buf, 0, (int) Math.min(buf.length, limit - total));
            if (r > 0) {
                proc = to.processBytes(buf, 0, r);
                total += r;
            } else if (r < 0) {
                break;
            }
        }
        return total;
    }

    /**
     * {@link #copy(ByteSource, ByteProcessor, long)} with a {@code limit} of
     * {@link Long#MAX_VALUE}
     */
    @ThreadLocalArray(8192)
    public static long copy(ByteSource from, ByteProcessor<?> to) throws IOException {
        return copy(from, to, Long.MAX_VALUE);
    }

    /**
     * Copies at most {@code limit} bytes from {@code from} into {@code to},
     * returning the total number of bytes copied. Byte processing stops on EOF
     * or when {@link ByteProcessor#processBytes(byte[], int, int) processBytes}
     * returns {@code false}.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @param limit the maximum number of bytes to copy
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @throws IllegalArgumentException if {@code limit} is negative
     */
    @ThreadLocalArray(8192)
    public static long copy(@NonNull ByteSource from, @NonNull ByteProcessor<?> to, long limit) throws IOException {
        checkLimit(limit);
        final Closer closer = Closer.create();
        try {
            return copy(closer.register(from.openStream()), to, limit);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Copies as many bytes as possible from {@code from} into {@code to},
     * returning the total number of bytes copied.
     * 
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @return the total number of bytes copied from {@code from} to {@code to}
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    @ThreadLocalArray(8192)
    public static int copy(@NonNull ByteSource from, @NonNull ByteBuffer to) throws IOException {
        final Closer closer = Closer.create();
        try {
            return copy(closer.register(from.openStream()), to);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Copies the entire contents of {@code from} into {@code to}.
     *
     * @param from the source to read bytes from
     * @param to the destination to copy bytes read from {@code from} into
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     */
    @ThreadLocalArray(8192)
    public static void copy(@NonNull ByteBuffer from, @NonNull ByteSink to) throws IOException {
        final Closer closer = Closer.create();
        try {
            OutputStream out = closer.register(to.openStream());
            copy(from, out);
            out.flush();
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Copies the entire contents of {@code from}, starting at index {@code off}
     * (inclusive) and ending at index {@code off + len} (exclusive), into
     * {@code to}. This provides an alternative to
     * {@link ByteSink#write(byte[])}.
     *
     * @param from the source to read bytes from
     * @param off the offset into {@code from} to start copying
     * @param len the number of bytes to copy starting at index {@code off}
     * @param to the destination to copy bytes read from {@code from} into
     * @throws IOException if an IOException occurs
     * @throws NullPointerException if either {@code from} or {@code to} is null
     * @see ByteSink#write(byte[])
     */
    public static void copy(byte[] from, int off, int len, @NonNull ByteSink to) throws IOException {
        checkPositionIndexes(off, off + len, from.length);
        final Closer closer = Closer.create();
        try {
            OutputStream out = closer.register(to.openStream());
            out.write(from, off, len);
            out.flush();
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    @ThreadLocalArray(8192)
    public static boolean contentEquals(ByteSource source, byte[] bytes) throws IOException {
        return contentEquals(source, bytes, 0, bytes.length);
    }

    @ThreadLocalArray(8192)
    public static boolean contentEquals(ByteSource source, byte[] bytes, int off, int len) throws IOException {
        return ChannelSource.contentEquals(source, bytes, off, len);
    }

    @ThreadLocalArray(8192)
    static boolean contentEqualsImpl(@NonNull ByteSource source, byte[] bytes, int off, int len)
            throws IOException {
        checkPositionIndexes(off, off + len, bytes.length);
        final Closer closer = Closer.create();
        try {
            return contentEquals(closer.register(source.openStream()), bytes, off, len);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    @ThreadLocalArray(8192)
    public static boolean contentEquals(ByteSource source1, ByteSource source2) throws IOException {
        return ChannelSource.contentEquals(source1, source2);
    }

    @ThreadLocalArray(8192)
    static boolean contentEqualsImpl(@NonNull ByteSource source1, @NonNull ByteSource source2) throws IOException {
        final Closer closer = Closer.create();
        try {
            return contentEquals(closer.register(source1.openStream()), closer.register(source2.openStream()));
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    @ThreadLocalArray(8192)
    public static boolean contentEquals(@NonNull ByteSource source1, @NonNull ByteBuffer buffer)
            throws IOException {
        final Closer closer = Closer.create();
        try {
            return contentEquals(closer.register(source1.openStream()), buffer);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    @ThreadLocalArray(8192)
    public static boolean contentEquals(@NonNull ByteSource source, @NonNull @WillNotClose InputStream in)
            throws IOException {
        final Closer closer = Closer.create();
        try {
            return contentEquals(closer.register(source.openStream()), in);
        } catch (Throwable e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Determines if the content of {@code in} equals the content in
     * {@code bytes}.
     */
    @ThreadLocalArray(8192)
    public static boolean contentEquals(@WillNotClose InputStream in, byte[] bytes) throws IOException {
        return contentEquals(in, bytes, 0, bytes.length);
    }

    /**
     * Determines if the content of {@code in} equals the content in the
     * specified range of {@code bytes}.
     */
    @ThreadLocalArray(8192)
    public static boolean contentEquals(@NonNull @WillNotClose InputStream in, byte[] bytes, int off, int len)
            throws IOException {
        checkPositionIndexes(off, off + len, bytes.length);
        final byte[] buf = threadLocalArray8K();
        int total = 0;
        while (true) {
            int r = in.read(buf);
            if (r > 0) {
                if (total + r > len) {
                    return false; //source is too long
                }
                if (!rangeEquals(bytes, off + total, r, buf, 0, r)) {
                    return false;
                }
                total += r;
            } else if (r < 0) {
                return total == len;
            }
        }
    }

    /**
     * Determines if the content of {@code in} equals the content in
     * {@code buf}.
     */
    @ThreadLocalArray(8192)
    public static boolean contentEquals(@WillNotClose InputStream in, ByteBuffer buf) throws IOException {
        if (buf.hasArray()) {
            return contentEquals(in, buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
        } else {
            return contentEquals(in, (InputStream) asInputStream(buf));
        }
    }

    /**
     * Determines if the content of {@code a} equals the content in {@code b}.
     */
    @ThreadLocalArray(8192)
    public static boolean contentEquals(@NonNull @WillNotClose InputStream a, @NonNull @WillNotClose InputStream b)
            throws IOException {
        final byte[] buf = threadLocalArray8K();
        while (true) {
            int ar = ByteStreams.read(a, buf, 0, 4096);
            int br = ByteStreams.read(b, buf, 4096, 4096);
            if (!rangeEquals(buf, 0, ar, buf, 4096, br)) {
                return false;
            }
            if (ar < 4096) {
                return true;
            }
        }
    }

    /**
     * Returns the remaining bytes in the buffer as a byte array. The position
     * of the ByteBuffer is not altered. If possible, the backing byte array
     * of the ByteBuffer is returned.
     * 
     * @param bb the ByteBuffer to convert to a byte array
     * @return the remaining bytes in {@code bb} as a byte array, returning
     * the backing byte array if possible
     */
    public static byte[] toByteArray(ByteBuffer bb) {
        if (bb.hasArray() && bb.arrayOffset() == 0 && bb.position() == 0) {
            final byte bytes[] = bb.array();
            if (bytes.length == bb.limit()) {
                return bytes;
            }
        }
        final byte bytes[] = new byte[bb.remaining()];
        final int pos = bb.position();
        bb.get(bytes);
        bb.position(pos);
        return bytes;
    }

    /**
     * Returns a ByteSource that will memoize/cache the contents of the
     * {@code source} parameter. The {@code source} stream will be read only
     * once. All subsequent reads will be read from an in memory cache of the
     * stream contents. The {@code source} ByteSource is not read immediately,
     * but is cached lazily on the first call to
     * {@link ByteSource#openStream()}.
     *
     * @param source the ByteSource to cache
     * @return a memoizing ByteSource wrapping {@code source}
     */
    public static ChannelSource memoize(ByteSource source) {
        return ChannelSource.memoize(source);
    }

    /**
     * Wraps the {@code b} byte array as a ByteSource. Same as
     * {@link #asInputStream(byte[], int, int) ByteUtils.asByteSource(b, 0, b.length)}
     * 
     * @see #asByteSource(byte[], int, int)
     * @param b the byte array to wrap
     * @return a ByteSource representing the byte array {@code b}
     */
    public static ChannelSource asByteSource(byte[] b) {
        return ChannelSource.of(b);
    }

    /**
     * Wraps the {@code b} byte array as a ByteSource. The ByteSource starts at
     * index {@code off} and has a size of {@code len} bytes. The returned
     * ByteSource overrides most ByteSource methods to take advantage of more
     * efficient implementations available using a byte array.
     * <p>
     * The ByteSource returned from this method differs from
     * {@link ByteSource#wrap(byte[])} in the following ways: <br>
     * <ul>
     *   <li>A range of the array may be specified</li>
     *   <li>The byte array returned by {@link ByteSource#read()} returns the
     *       backing byte array, not a copy of the backing array. Therefore,
     *       changes to the returned array will be reflected in the ByteSource.
     *   </li>
     *   <li>The InputStreams are {@link BAInputStream}s, not
     *       {@link java.io.ByteArrayInputStream}. The only difference is that
     *       the methods are not synchronized.
     *   </li>
     *   <li>The {@link ByteSource#slice(long, long)} has the same behavior as
     *       calling this method on a sub-range of the byte array. This is more
     *       efficient than using the default ByteSource#SlicedByteSource
     *       ByteSource.
     *   </li>
     *   <li>{@link ByteSource#contentEquals(com.google.common.io.ByteSource)}
     *       and {@link ByteSource#copyTo(com.google.common.io.ByteSink)} are
     *       overridden to provide more efficient implementation.
     *   </li>
     * </ul>
     * 
     * @param b the byte array to wrap
     * @param off the first index into {@code b} that the ByteSource stream
     * will contain
     * @param len the length of the ByteSource stream starting at index {@code off}
     * @return a ByteSource representing a range of bytes in {@code b}
     */
    public static ChannelSource asByteSource(byte[] b, int off, int len) {
        return ChannelSource.of(b, off, len);
    }

    /**
     * Wraps a ByteBuffer as a ByteSource. Opening a stream will return a
     * {@link ByteBufferInputStream}. A read duplicate of the {@code buffer}
     * argument is made; therefore, use of the returned ByteSource will not
     * affect the state of the provided ByteBuffer.
     * <p>
     * The returned ByteSource overrides most ByteSource methods to take
     * advantage of more efficient implementations available using a ByteBuffer.
     *
     * @param buffer the ByteBuffer to wrap
     * @return a ByteSource representing ByteBuffer array {@code b}
     */
    public static ChannelSource asByteSource(ByteBuffer buffer) {
        return ChannelSource.of(buffer);
    }

    /**
     * Returns a singleton empty ByteSource instance <p>
     * This differs from {@link ByteSource#empty()} in that the returned
     * ByteSource is its own class with more optimizations, whereas
     * {@link ByteSource#empty()} returns a ByteArrayByteSource with a zero
     * length array.
     * 
     * @return a singleton ByteSource with an empty stream
     */
    public static ChannelSource emptyByteSource() {
        return ChannelSource.empty();
    }

    public static BAInputStream asInputStream(byte[] bytes) {
        return new BAInputStream(bytes);
    }

    public static BAInputStream asInputStream(byte[] bytes, int off, int len) {
        return new BAInputStream(bytes, off, len);
    }

    public static ByteBufferInputStream asInputStream(ByteBuffer buffer) {
        return new ByteBufferInputStream(buffer);
    }

    public static InputStream asInputStream(ReadableByteChannel in) {
        return in instanceof InputStream ? (InputStream) in : new ReadableByteChannelInputStream(in);
    }

    public static ReadableByteChannel asReadableByteChannel(InputStream in) {
        return in instanceof ReadableByteChannel ? (ReadableByteChannel) in
                : new InputStreamReadableByteChannel(in);
    }

    public static OutputStream asOutputStream(WritableByteChannel out) {
        return out instanceof OutputStream ? (OutputStream) out : new WritableByteChannelOutputStream(out);
    }

    public static WritableByteChannel asWritableByteChannel(OutputStream out) {
        return out instanceof WritableByteChannel ? (WritableByteChannel) out
                : new OutputStreamWritableByteChannel(out);
    }

    static final class ReadableByteChannelInputStream extends InputStream implements ReadableByteChannel {

        @Delegate
        private final ReadableByteChannel delegate;
        private ByteBuffer single;

        public ReadableByteChannelInputStream(ReadableByteChannel delegate) {
            this.delegate = checkNotNull(delegate);
        }

        private void ensureOpen() throws IOException {
            if (!delegate.isOpen()) {
                throw new IOException("channel closed");
            }
        }

        @Override
        public int read() throws IOException {
            if (single == null) {
                single = ByteBuffer.allocate(1);
            } else {
                single.clear();
            }
            int r;
            do {
                r = delegate.read(single);
            } while (r == 0);
            return r < 0 ? r : (single.get(0) & 0xFF);
        }

        @Override
        public long skip(long n) throws IOException {
            ensureOpen();
            if (n <= 0) {
                return 0;
            }
            final ByteBuffer buf = threadLocalBuffer8K();
            long total = 0;
            do {
                buf.limit((int) Math.min(buf.capacity(), n - total));
                int r = delegate.read(buf);
                if (r > 0) {
                    total += r;
                    buf.clear();
                } else if (r < 0) {
                    break;
                }
            } while (total < n);
            return total;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            checkPositionIndexes(off, off + len, b.length);
            return delegate.read(ByteBuffer.wrap(b, off, len));
        }

        @Override
        public void mark(int readlimit) {
            checkArgument(readlimit >= 0, "readlimit (%s) may not be negative", readlimit);
            throw new UnsupportedOperationException();
        }

        @Override
        public int available() throws IOException {
            ensureOpen();
            return 0;
        }
    }

    static final class InputStreamReadableByteChannel extends InputStream implements ReadableByteChannel {

        @Delegate(excludes = Closeable.class)
        private final InputStream delegate;
        private boolean closed;

        public InputStreamReadableByteChannel(InputStream delegate) {
            this.delegate = checkNotNull(delegate);
        }

        @Override
        public void close() throws IOException {
            if (!closed) {
                delegate.close();
                closed = true;
            }
        }

        @Override
        public int read(ByteBuffer dst) throws IOException {
            if (dst.hasRemaining()) {
                final int total = copy(delegate, dst);
                return total > 0 ? total : -1;
            } else if (closed) {
                throw new IOException("stream closed");
            } else {
                return 0;
            }
        }

        @Override
        public boolean isOpen() {
            return !closed;
        }
    }

    static final class WritableByteChannelOutputStream extends OutputStream implements WritableByteChannel {

        @Delegate
        final WritableByteChannel delegate;
        private ByteBuffer single;

        public WritableByteChannelOutputStream(WritableByteChannel delegate) {
            this.delegate = checkNotNull(delegate);
        }

        @Override
        public void write(int b) throws IOException {
            if (single == null) {
                single = ByteBuffer.allocate(1);
            } else {
                single.clear();
            }
            single.put(0, (byte) b);
            copy(single, delegate);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            checkPositionIndexes(off, off + len, b.length);
            copy(ByteBuffer.wrap(b, off, len), delegate);
        }

        @Override
        public void flush() throws IOException {
            if (delegate instanceof Flushable) {
                ((Flushable) delegate).flush();
            }
        }
    }

    static final class OutputStreamWritableByteChannel extends OutputStream implements WritableByteChannel {

        @Delegate(excludes = Closeable.class)
        final OutputStream delegate;
        private boolean closed;

        public OutputStreamWritableByteChannel(OutputStream delegate) {
            this.delegate = checkNotNull(delegate);
        }

        @Override
        public int write(ByteBuffer src) throws IOException {
            final int total = src.remaining();
            if (total > 0) {
                copy(src, delegate);
                assert !src.hasRemaining();
            } else if (closed) {
                throw new IOException("stream closed");
            }
            return total;
        }

        @Override
        public boolean isOpen() {
            return !closed;
        }

        @Override
        public void close() throws IOException {
            if (!closed) {
                delegate.close();
                closed = true;
            }
        }
    }

    /**
     * Wraps a {@code DataOutput} as an {@code OutputStream}. If
     * {@code delegate} is already an {@code OutputStream}, it is returned as
     * is.
     */
    public static OutputStream asOutputStream(DataOutput delegate) {
        return delegate instanceof OutputStream ? (OutputStream) delegate : new DataOutputOutputStream(delegate);
    }

    static class DataOutputOutputStream extends OutputStream {

        final DataOutput delegate;

        public DataOutputOutputStream(DataOutput delegate) {
            this.delegate = checkNotNull(delegate);
        }

        @Override
        public void write(int b) throws IOException {
            delegate.write(b);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            delegate.write(b, off, len);
        }

        @Override
        public void write(byte[] b) throws IOException {
            delegate.write(b);
        }

        @Override
        public String toString() {
            return delegate.toString();
        }
    }

    /**
     * Wraps the {@link ByteProcessor} as an {@code OutputStream}.
     * 
     * @param processor the {@link ByteProcessor} to wrap
     * @return {@code processor} as an {@code OutputStream}
     */
    public static OutputStream asOutputStream(ByteProcessor<?> processor) {
        return new ByteProcessorOutputStream(processor);
    }

    static class ByteProcessorOutputStream extends OutputStream {
        final ByteProcessor<?> processor;
        private byte[] single;

        public ByteProcessorOutputStream(ByteProcessor<?> processor) {
            this.processor = checkNotNull(processor);
        }

        @Override
        public void write(int b) throws IOException {
            if (single == null) {
                single = new byte[1];
            }
            single[0] = (byte) b;
            processor.processBytes(single, 0, 1);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            processor.processBytes(b, off, len);
        }

        @Override
        public void write(byte[] b) throws IOException {
            processor.processBytes(b, 0, b.length);
        }

        @Override
        public String toString() {
            return processor.toString();
        }
    }

    /**
     * Wraps {@code resource} in a Closeable that will flush and close
     * {@code resource} when it is closed. This ensures that {@code resource} is
     * always flushed when closed.
     *
     * @param resource the Closeable & Flushable resource to wrap
     * @return a Closeable that will close and flush {@code resource} when
     * closed
     */
    public static <T extends Closeable & Flushable> Closeable flushOnClose(final @Nullable T resource) {
        return new Closeable() {
            private boolean closed;

            @Override
            @SuppressWarnings({ "BroadCatchBlock", "TooBroadCatch", "unchecked" })
            public void close() throws IOException {
                if (!closed && resource != null
                        && (!(resource instanceof Channel) || ((Channel) resource).isOpen())) {
                    closed = true;
                    final Closer closer = Closer.create();
                    try {
                        closer.register(resource);
                        resource.flush();
                    } catch (Throwable e) {
                        throw closer.rethrow(e);
                    } finally {
                        closer.close();
                    }
                }
            }
        };
    }

    /**
     * Wraps the delegate {@link OutputStream} as a {@link ByteProcessor}.
     * {@link ByteProcessor#getResult()} will return the result generated from
     * the {@code resultFunction} when passed the {@code delegate} OutputStream
     * as the input to the Function.
     * 
     * @see #asByteProcessor(OutputStream)
     * @param <O> the type of the OutputStream
     * @param <R> the result type that will be generated from the OutputStream
     * @param delegate the OutputStream to wrap
     * @param resultFunction the function that will generate the result from
     * the {@code delegate} OutputStream
     * @return {@code delegate} as a ByteProcessor
     */
    public static <O extends OutputStream, R> ByteProcessor<R> asByteProcessor(O delegate,
            Function<? super O, ? extends R> resultFunction) {
        return new OutputStreamByteProcessor<O, R>(delegate, checkNotNull(resultFunction));
    }

    /**
     * Wraps the delegate {@link OutputStream} as a {@link ByteProcessor}.
     * {@link ByteProcessor#getResult()} will return null.
     * 
     * @see #asByteProcessor(OutputStream, Function)
     * @param delegate the OutputStream to wrap
     * @return {@code delegate} as a ByteProcessor
     */
    public static ByteProcessor<?> asByteProcessor(OutputStream delegate) {
        return new OutputStreamByteProcessor<OutputStream, Object>(delegate, null);
    }

    static class OutputStreamByteProcessor<O extends OutputStream, R> implements ByteProcessor<R> {

        final O delegate;
        @Nullable
        final Function<? super O, ? extends R> toResult;

        public OutputStreamByteProcessor(O delegate, @Nullable Function<? super O, ? extends R> toResult) {
            this.delegate = checkNotNull(delegate);
            this.toResult = toResult;
        }

        @Override
        public boolean processBytes(byte[] buf, int off, int len) throws IOException {
            delegate.write(buf, off, len);
            return true;
        }

        @Override
        public R getResult() {
            return toResult != null ? toResult.apply(delegate) : null;
        }
    }

    /**
     * Wraps the HashFunction as a ByteProcessor. {@code hashFunction} is
     * converted to a {@link Hasher} using {@link HashFunction#newHasher()}. The
     * {@link ByteProcessor#getResult()} returns the result of
     * {@link Hasher#hash()}. Once the result has been generated, any further
     * calls to {@link ByteProcessor#processBytes(byte[], int, int)} will throw
     * an {@link IllegalStateException}.
     *
     * @param hashFunction the hashFunction to wrap
     * @return {@code hashFunction} as a ByteSource
     */
    public static ByteProcessor<HashCode> asByteProcessor(HashFunction hashFunction) {
        return new HashingByteProcessor(hashFunction);
    }

    /**
     * Wraps the HashFunction as a ByteProcessor. {@code hashFunction} is
     * converted to a {@link Hasher} using {@link HashFunction#newHasher(int)}.
     * The {@link ByteProcessor#getResult()} returns the result of
     * {@link Hasher#hash()}. Once the result has been generated, any further
     * calls to {@link ByteProcessor#processBytes(byte[], int, int)} will throw
     * an {@link IllegalStateException}.
     *
     * @param hashFunction the hashFunction to wrap
     * @param expectedInputSize a hint of the expected size of the input (in bytes)
     * @return {@code hashFunction} as a ByteSource
     */
    public static ByteProcessor<HashCode> asByteProcessor(HashFunction hashFunction, int expectedInputSize) {
        return new HashingByteProcessor(hashFunction, expectedInputSize);
    }

    /**
     * Wraps the Hasher as a ByteProcessor. The
     * {@link ByteProcessor#getResult()} returns the result of
     * {@link Hasher#hash()}. Once the result has been generated, any further
     * calls to {@link ByteProcessor#processBytes(byte[], int, int)} will throw
     * an {@link IllegalStateException}.
     *
     * @param hasher the Hasher to wrap
     * @return {@code haser} as a ByteSource
     */
    public static ByteProcessor<HashCode> asByteProcessor(Hasher hasher) {
        return new HashingByteProcessor(hasher);
    }

    static class HashingByteProcessor implements ByteProcessor<HashCode> {

        final Hasher hasher;
        HashCode result;

        public HashingByteProcessor(HashFunction hashFunction) {
            this.hasher = hashFunction.newHasher();
        }

        public HashingByteProcessor(HashFunction hashFunction, int expectedInputSize) {
            this.hasher = hashFunction.newHasher(expectedInputSize);
        }

        public HashingByteProcessor(Hasher hasher) {
            this.hasher = checkNotNull(hasher);
        }

        @Override
        public boolean processBytes(byte[] buf, int off, int len) throws IOException {
            checkState(result == null, "getResult() has been called");
            hasher.putBytes(buf, off, len);
            return true;
        }

        @Override
        public HashCode getResult() {
            if (result == null) {
                result = hasher.hash();
            }
            return result;
        }
    }

    /**
     * Copies the bytes between the position and limit of the provided buffer
     * into a new ByteBuffer. The returned buffer's position is 0 and its limit
     * and capacity will be equal to {@code buffer.remaining()}. The provided
     * {@code buffer} (including its mark) is left unchanged.
     *
     * @param buffer the buffer to copy
     * @return a new ByteBuffer that is a copy of the {@code buffer.remaining()}
     * bytes of the provided buffer
     */
    public static ByteBuffer deepCopy(ByteBuffer buffer) {
        final int p = buffer.position();
        final ByteBuffer copy = ByteBuffer.allocate(buffer.remaining());
        copy.put(buffer).rewind();
        buffer.position(p);
        return copy;
    }

    static final Function<ByteBuffer, byte[]> BUFFER_TO_ARRAY = new Function<ByteBuffer, byte[]>() {
        @Override
        public byte[] apply(ByteBuffer input) {
            return input != null ? toByteArray(input) : null;
        }
    };

    static final Function<byte[], ByteBuffer> ARRAY_TO_BUFFER = new Function<byte[], ByteBuffer>() {
        @Override
        public ByteBuffer apply(byte[] input) {
            return input != null ? ByteBuffer.wrap(input) : null;
        }
    };

    public static Function<ByteBuffer, byte[]> bufferToArray() {
        return BUFFER_TO_ARRAY;
    }

    public static Function<byte[], ByteBuffer> arrayToBuffer() {
        return ARRAY_TO_BUFFER;
    }

    public static OutputStream nullOutputStream() {
        return NullOutputStream.INSTANCE;
    }

    public static WritableByteChannel nullWritableByteChannel() {
        return NullOutputStream.INSTANCE;
    }

    static class NullOutputStream extends OutputStream implements WritableByteChannel {
        static final NullOutputStream INSTANCE = new NullOutputStream();

        @Override
        public void write(@NonNull byte[] b, int off, int len) {
            checkPositionIndexes(off, off + len, b.length);
        }

        @Override
        public void write(@NonNull byte[] b) {
        }

        @Override
        public void write(int b) {
        }

        @Override
        public int write(@NonNull ByteBuffer src) {
            final int w = src.remaining();
            src.position(src.limit());
            return w;
        }

        @Override
        public boolean isOpen() {
            return true;
        }
    }

    /**
     * Returns a {@code InputStream} that delegates to {@code in} but ensures
     * {@code runWhenComplete} is run exactly once when closed or on EOF
     * (whichever comes first).
     */
    public static InputStream runWhenComplete(@NonNull final InputStream in,
            @NonNull final Runnable runWhenComplete) {
        return new InputStream() {
            volatile boolean run;

            private synchronized void doRun() {
                if (!run) {
                    run = true;
                    runWhenComplete.run();
                }
            }

            private int tryRun(int r) {
                if (r < 0 && !run) {
                    doRun();
                }
                return r;
            }

            @Override
            public int read() throws IOException {
                return tryRun(in.read());
            }

            @Override
            public int read(byte[] b, int off, int len) throws IOException {
                return tryRun(in.read(b, off, len));
            }

            @Override
            public void close() throws IOException {
                try {
                    in.close();
                } finally {
                    if (!run) {
                        doRun();
                    }
                }
            }

            @Override
            public boolean markSupported() {
                return in.markSupported();
            }

            @Override
            public void reset() throws IOException {
                in.reset();
            }

            @Override
            public void mark(int readlimit) {
                in.mark(readlimit);
            }

            @Override
            public int available() throws IOException {
                return in.available();
            }

            @Override
            public long skip(long n) throws IOException {
                return in.skip(n);
            }

            @Override
            public String toString() {
                return in.toString();
            }
        };
    }

    public static DecodingChannel synchronizedDecodingChannel(DecodingChannel channel) {
        return new SynchronizedDecodingChannel(channel, channel);
    }

    public static DecodingChannel synchronizedDecodingChannel(DecodingChannel channel, Object mutex) {
        return new SynchronizedDecodingChannel(channel, mutex);
    }

    private static class SynchronizedDecodingChannel implements DecodingChannel {

        final DecodingChannel delegate;
        final Object mutex;

        public SynchronizedDecodingChannel(DecodingChannel delegate, Object mutex) {
            this.delegate = checkNotNull(delegate);
            this.mutex = checkNotNull(mutex);
        }

        @Override
        @Synchronized("mutex")
        public ExtendedCharSequence content() {
            return StringUtils.synchronizedExtendedCharSequence(delegate.content(), mutex);
        }

        @Override
        @Synchronized("mutex")
        public void clearContent() {
            delegate.clearContent();
        }

        @Override
        @Synchronized("mutex")
        public Charset charset() {
            return delegate.charset();
        }

        @Override
        @Synchronized("mutex")
        public int write(ByteBuffer src) throws IOException {
            return delegate.write(src);
        }

        @Override
        @Synchronized("mutex")
        public boolean isOpen() {
            return delegate.isOpen();
        }

        @Override
        @Synchronized("mutex")
        public void close() throws IOException {
            delegate.close();
        }

        @Override
        @Synchronized("mutex")
        public ListenableFuture<String> charsetFuture() {
            return delegate.charsetFuture();
        }

        @Override
        @Synchronized("mutex")
        public CharsetDetector duplicate() {
            CharsetDetector det = delegate.duplicate();
            if (det == delegate) {
                return this;
            }
            if (det instanceof DecodingChannel) {
                return new SynchronizedDecodingChannel((DecodingChannel) det, mutex);
            }
            return det;
        }

        @Override
        @Synchronized("mutex")
        public String toString() {
            return delegate.toString();
        }
    }

    /**
     * Returns a {@code Supplier} that returns a duplicate of {@code buffer}.
     * The {@code Supplier} returns a "snapshot" of {@code buffer} at the time
     * of this method invocation (using
     * {@link ByteBuffer#duplicate() duplicate()}); therefore, changes made to
     * {@code buffer} after this method has been called have no effect on the
     * {@code Supplier}.
     */
    public static Supplier<ByteBuffer> supplier(ByteBuffer buffer) {
        final ByteBuffer delegate = buffer.duplicate();
        return new Supplier<ByteBuffer>() {
            @Override
            public ByteBuffer get() {
                return delegate.duplicate();
            }
        };
    }
}