/* * Copyright (C) 2007 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.common.internal; import java.io.ByteArrayOutputStream; import java.io.DataInput; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import static com.facebook.common.internal.Preconditions.checkNotNull; /** * Provides utility methods for working with byte arrays and I/O streams. * * @author Chris Nokleberg * @author Colin Decker * @since 1.0 */ public final class ByteStreams { private static final int BUF_SIZE = 0x1000; // 4K private ByteStreams() { } /** * Copies all bytes from the input stream to the output stream. Does not close or flush either * stream. * * @param from the input stream to read from * @param to the output stream to write to * @return the number of bytes copied * @throws IOException if an I/O error occurs */ public static long copy(InputStream from, OutputStream to) throws IOException { checkNotNull(from); checkNotNull(to); byte[] buf = new byte[BUF_SIZE]; long total = 0; while (true) { int r = from.read(buf); if (r == -1) { break; } to.write(buf, 0, r); total += r; } return total; } /** * Reads some bytes from an input stream and stores them into the buffer array * {@code b}. This method blocks until {@code len} bytes of input data have * been read into the array, or end of file is detected. The number of bytes * read is returned, possibly zero. Does not close the stream. * * <p>A caller can detect EOF if the number of bytes read is less than * {@code len}. All subsequent calls on the same stream will return zero. * * <p>If {@code b} is null, a {@code NullPointerException} is thrown. If * {@code off} is negative, or {@code len} is negative, or {@code off+len} is * greater than the length of the array {@code b}, then an * {@code IndexOutOfBoundsException} is thrown. If {@code len} is zero, then * no bytes are read. Otherwise, the first byte read is stored into element * {@code b[off]}, the next one into {@code b[off+1]}, and so on. The number * of bytes read is, at most, equal to {@code len}. * * @param in the input stream to read from * @param b the buffer into which the data is read * @param off an int specifying the offset into the data * @param len an int specifying the number of bytes to read * @return the number of bytes read * @throws IOException if an I/O error occurs */ public static int read(InputStream in, byte[] b, int off, int len) throws IOException { checkNotNull(in); checkNotNull(b); if (len < 0) { throw new IndexOutOfBoundsException("len is negative"); } int total = 0; while (total < len) { int result = in.read(b, off + total, len - total); if (result == -1) { break; } total += result; } return total; } /** * Reads all bytes from an input stream into a byte array. * Does not close the stream. * * @param in the input stream to read from * @return a byte array containing all the bytes from the stream * @throws IOException if an I/O error occurs */ public static byte[] toByteArray(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); copy(in, out); return out.toByteArray(); } /** * Reads all bytes from an input stream into a byte array. The given * expected size is used to create an initial byte array, but if the actual * number of bytes read from the stream differs, the correct result will be * returned anyway. */ public static byte[] toByteArray( InputStream in, int expectedSize) throws IOException { byte[] bytes = new byte[expectedSize]; int remaining = expectedSize; while (remaining > 0) { int off = expectedSize - remaining; int read = in.read(bytes, off, remaining); if (read == -1) { // end of stream before reading expectedSize bytes // just return the bytes read so far return Arrays.copyOf(bytes, off); } remaining -= read; } // bytes is now full int b = in.read(); if (b == -1) { return bytes; } // the stream was longer, so read the rest normally FastByteArrayOutputStream out = new FastByteArrayOutputStream(); out.write(b); // write the byte we read when testing for end of stream copy(in, out); byte[] result = new byte[bytes.length + out.size()]; System.arraycopy(bytes, 0, result, 0, bytes.length); out.writeTo(result, bytes.length); return result; } /** * BAOS that provides limited access to its internal byte array. */ private static final class FastByteArrayOutputStream extends ByteArrayOutputStream { /** * Writes the contents of the internal buffer to the given array starting * at the given offset. Assumes the array has space to hold count bytes. */ void writeTo(byte[] b, int off) { System.arraycopy(buf, 0, b, off, count); } } /** * Attempts to read {@code len} bytes from the stream into the given array * starting at {@code off}, with the same behavior as * {@link DataInput#readFully(byte[], int, int)}. Does not close the * stream. * * @param in the input stream to read from. * @param b the buffer into which the data is read. * @param off an int specifying the offset into the data. * @param len an int specifying the number of bytes to read. * @throws EOFException if this stream reaches the end before reading all * the bytes. * @throws IOException if an I/O error occurs. */ public static void readFully( InputStream in, byte[] b, int off, int len) throws IOException { int read = read(in, b, off, len); if (read != len) { throw new EOFException("reached end of stream after reading " + read + " bytes; " + len + " bytes expected"); } } }