Java tutorial
/* 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(); } }; } }