    001    /*
    002     * Copyright (C) 2007 The Guava Authors
    003     *
    004     * Licensed under the Apache License, Version 2.0 (the "License");
    005     * you may not use this file except in compliance with the License.
    006     * You may obtain a copy of the License at
    007     *
    008     * http://www.apache.org/licenses/LICENSE-2.0
    009     *
    010     * Unless required by applicable law or agreed to in writing, software
    011     * distributed under the License is distributed on an "AS IS" BASIS,
    012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    013     * See the License for the specific language governing permissions and
    014     * limitations under the License.
    015     */
    016
    017    package com.google.common.io;
    018
    019    import com.google.common.annotations.Beta;
    020    import com.google.common.base.Preconditions;
    021    import com.google.common.hash.Funnels;
    022    import com.google.common.hash.HashCode;
    023    import com.google.common.hash.HashFunction;
    024    import com.google.common.hash.Hasher;
    025
    026    import java.io.ByteArrayInputStream;
    027    import java.io.ByteArrayOutputStream;
    028    import java.io.DataInput;
    029    import java.io.DataInputStream;
    030    import java.io.DataOutput;
    031    import java.io.DataOutputStream;
    032    import java.io.EOFException;
    033    import java.io.IOException;
    034    import java.io.InputStream;
    035    import java.io.OutputStream;
    036    import java.nio.ByteBuffer;
    037    import java.nio.channels.ReadableByteChannel;
    038    import java.nio.channels.WritableByteChannel;
    039    import java.util.Arrays;
    040    import java.util.zip.Checksum;
    041
    042    /**
    043     * Provides utility methods for working with byte arrays and I/O streams.
    044     *
    045     * @author Chris Nokleberg
    046     * @since 1.0
    047     */
    048    @Beta
    049    public final class ByteStreams {
    050      private static final int BUF_SIZE = 0x1000; // 4K
    051
    052      private ByteStreams() {}
    053
    054      /**
    055       * Returns a factory that will supply instances of
    056       * {@link ByteArrayInputStream} that read from the given byte array.
    057       *
    058       * @param b the input buffer
    059       * @return the factory
    060       */
    061      public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier(
    062          byte[] b) {
    063        return newInputStreamSupplier(b, 0, b.length);
    064      }
    065
    066      /**
    067       * Returns a factory that will supply instances of
    068       * {@link ByteArrayInputStream} that read from the given byte array.
    069       *
    070       * @param b the input buffer
    071       * @param off the offset in the buffer of the first byte to read
    072       * @param len the maximum number of bytes to read from the buffer
    073       * @return the factory
    074       */
    075      public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier(
    076          final byte[] b, final int off, final int len) {
    077        return new InputSupplier<ByteArrayInputStream>() {
    078          @Override
    079          public ByteArrayInputStream getInput() {
    080            return new ByteArrayInputStream(b, off, len);
    081          }
    082        };
    083      }
    084
    085      /**
    086       * Writes a byte array to an output stream from the given supplier.
    087       *
    088       * @param from the bytes to write
    089       * @param to the output supplier
    090       * @throws IOException if an I/O error occurs
    091       */
    092      public static void write(byte[] from,
    093          OutputSupplier<? extends OutputStream> to) throws IOException {
    094        Preconditions.checkNotNull(from);
    095        boolean threw = true;
    096        OutputStream out = to.getOutput();
    097        try {
    098          out.write(from);
    099          threw = false;
    100        } finally {
    101          Closeables.close(out, threw);
    102        }
    103      }
    104
    105      /**
    106       * Opens input and output streams from the given suppliers, copies all
    107       * bytes from the input to the output, and closes the streams.
    108       *
    109       * @param from the input factory
    110       * @param to the output factory
    111       * @return the number of bytes copied
    112       * @throws IOException if an I/O error occurs
    113       */
    114      public static long copy(InputSupplier<? extends InputStream> from,
    115          OutputSupplier<? extends OutputStream> to) throws IOException {
    116        int successfulOps = 0;
    117        InputStream in = from.getInput();
    118        try {
    119          OutputStream out = to.getOutput();
    120          try {
    121            long count = copy(in, out);
    122            successfulOps++;
    123            return count;
    124          } finally {
    125            Closeables.close(out, successfulOps < 1);
    126            successfulOps++;
    127          }
    128        } finally {
    129          Closeables.close(in, successfulOps < 2);
    130        }
    131      }
    132
    133      /**
    134       * Opens an input stream from the supplier, copies all bytes from the
    135       * input to the output, and closes the input stream. Does not close
    136       * or flush the output stream.
    137       *
    138       * @param from the input factory
    139       * @param to the output stream to write to
    140       * @return the number of bytes copied
    141       * @throws IOException if an I/O error occurs
    142       */
    143      public static long copy(InputSupplier<? extends InputStream> from,
    144          OutputStream to) throws IOException {
    145        boolean threw = true;
    146        InputStream in = from.getInput();
    147        try {
    148          long count = copy(in, to);
    149          threw = false;
    150          return count;
    151        } finally {
    152          Closeables.close(in, threw);
    153        }
    154      }
    155
    156      /**
    157       * Opens an output stream from the supplier, copies all bytes from the input
    158       * to the output, and closes the output stream. Does not close or flush the
    159       * input stream.
    160       *
    161       * @param from the input stream to read from
    162       * @param to the output factory
    163       * @return the number of bytes copied
    164       * @throws IOException if an I/O error occurs
    165       * @since 10.0
    166       */
    167      public static long copy(InputStream from,
    168          OutputSupplier<? extends OutputStream> to) throws IOException {
    169        boolean threw = true;
    170        OutputStream out = to.getOutput();
    171        try {
    172          long count = copy(from, out);
    173          threw = false;
    174          return count;
    175        } finally {
    176          Closeables.close(out, threw);
    177        }
    178      }
    179
    180      /**
    181       * Copies all bytes from the input stream to the output stream.
    182       * Does not close or flush either stream.
    183       *
    184       * @param from the input stream to read from
    185       * @param to the output stream to write to
    186       * @return the number of bytes copied
    187       * @throws IOException if an I/O error occurs
    188       */
    189      public static long copy(InputStream from, OutputStream to)
    190          throws IOException {
    191        byte[] buf = new byte[BUF_SIZE];
    192        long total = 0;
    193        while (true) {
    194          int r = from.read(buf);
    195          if (r == -1) {
    196            break;
    197          }
    198          to.write(buf, 0, r);
    199          total += r;
    200        }
    201        return total;
    202      }
    203
    204      /**
    205       * Copies all bytes from the readable channel to the writable channel.
    206       * Does not close or flush either channel.
    207       *
    208       * @param from the readable channel to read from
    209       * @param to the writable channel to write to
    210       * @return the number of bytes copied
    211       * @throws IOException if an I/O error occurs
    212       */
    213      public static long copy(ReadableByteChannel from,
    214          WritableByteChannel to) throws IOException {
    215        ByteBuffer buf = ByteBuffer.allocate(BUF_SIZE);
    216        long total = 0;
    217        while (from.read(buf) != -1) {
    218          buf.flip();
    219          while (buf.hasRemaining()) {
    220            total += to.write(buf);
    221          }
    222          buf.clear();
    223        }
    224        return total;
    225      }
    226
    227      /**
    228       * Reads all bytes from an input stream into a byte array.
    229       * Does not close the stream.
    230       *
    231       * @param in the input stream to read from
    232       * @return a byte array containing all the bytes from the stream
    233       * @throws IOException if an I/O error occurs
    234       */
    235      public static byte[] toByteArray(InputStream in) throws IOException {
    236        ByteArrayOutputStream out = new ByteArrayOutputStream();
    237        copy(in, out);
    238        return out.toByteArray();
    239      }
    240
    241      /**
    242       * Returns the data from a {@link InputStream} factory as a byte array.
    243       *
    244       * @param supplier the factory
    245       * @throws IOException if an I/O error occurs
    246       */
    247      public static byte[] toByteArray(
    248          InputSupplier<? extends InputStream> supplier) throws IOException {
    249        boolean threw = true;
    250        InputStream in = supplier.getInput();
    251        try {
    252          byte[] result = toByteArray(in);
    253          threw = false;
    254          return result;
    255        } finally {
    256          Closeables.close(in, threw);
    257        }
    258      }
    259
    260      /**
    261       * Returns a new {@link ByteArrayDataInput} instance to read from the {@code
    262       * bytes} array from the beginning.
    263       */
    264      public static ByteArrayDataInput newDataInput(byte[] bytes) {
    265        return new ByteArrayDataInputStream(bytes);
    266      }
    267
    268      /**
    269       * Returns a new {@link ByteArrayDataInput} instance to read from the {@code
    270       * bytes} array, starting at the given position.
    271       *
    272       * @throws IndexOutOfBoundsException if {@code start} is negative or greater
    273       *     than the length of the array
    274       */
    275      public static ByteArrayDataInput newDataInput(byte[] bytes, int start) {
    276        Preconditions.checkPositionIndex(start, bytes.length);
    277        return new ByteArrayDataInputStream(bytes, start);
    278      }
    279
    280      private static class ByteArrayDataInputStream implements ByteArrayDataInput {
    281        final DataInput input;
    282
    283        ByteArrayDataInputStream(byte[] bytes) {
    284          this.input = new DataInputStream(new ByteArrayInputStream(bytes));
    285        }
    286
    287        ByteArrayDataInputStream(byte[] bytes, int start) {
    288          this.input = new DataInputStream(
    289              new ByteArrayInputStream(bytes, start, bytes.length - start));
    290        }
    291
    292        @Override public void readFully(byte b[]) {
    293          try {
    294            input.readFully(b);
    295          } catch (IOException e) {
    296            throw new IllegalStateException(e);
    297          }
    298        }
    299
    300        @Override public void readFully(byte b[], int off, int len) {
    301          try {
    302            input.readFully(b, off, len);
    303          } catch (IOException e) {
    304            throw new IllegalStateException(e);
    305          }
    306        }
    307
    308        @Override public int skipBytes(int n) {
    309          try {
    310            return input.skipBytes(n);
    311          } catch (IOException e) {
    312            throw new IllegalStateException(e);
    313          }
    314        }
    315
    316        @Override public boolean readBoolean() {
    317          try {
    318            return input.readBoolean();
    319          } catch (IOException e) {
    320            throw new IllegalStateException(e);
    321          }
    322        }
    323
    324        @Override public byte readByte() {
    325          try {
    326            return input.readByte();
    327          } catch (EOFException e) {
    328            throw new IllegalStateException(e);
    329          } catch (IOException impossible) {
    330            throw new AssertionError(impossible);
    331          }
    332        }
    333
    334        @Override public int readUnsignedByte() {
    335          try {
    336            return input.readUnsignedByte();
    337          } catch (IOException e) {
    338            throw new IllegalStateException(e);
    339          }
    340        }
    341
    342        @Override public short readShort() {
    343          try {
    344            return input.readShort();
    345          } catch (IOException e) {
    346            throw new IllegalStateException(e);
    347          }
    348        }
    349
    350        @Override public int readUnsignedShort() {
    351          try {
    352            return input.readUnsignedShort();
    353          } catch (IOException e) {
    354            throw new IllegalStateException(e);
    355          }
    356        }
    357
    358        @Override public char readChar() {
    359          try {
    360            return input.readChar();
    361          } catch (IOException e) {
    362            throw new IllegalStateException(e);
    363          }
    364        }
    365
    366        @Override public int readInt() {
    367          try {
    368            return input.readInt();
    369          } catch (IOException e) {
    370            throw new IllegalStateException(e);
    371          }
    372        }
    373
    374        @Override public long readLong() {
    375          try {
    376            return input.readLong();
    377          } catch (IOException e) {
    378            throw new IllegalStateException(e);
    379          }
    380        }
    381
    382        @Override public float readFloat() {
    383          try {
    384            return input.readFloat();
    385          } catch (IOException e) {
    386            throw new IllegalStateException(e);
    387          }
    388        }
    389
    390        @Override public double readDouble() {
    391          try {
    392            return input.readDouble();
    393          } catch (IOException e) {
    394            throw new IllegalStateException(e);
    395          }
    396        }
    397
    398        @Override public String readLine() {
    399          try {
    400            return input.readLine();
    401          } catch (IOException e) {
    402            throw new IllegalStateException(e);
    403          }
    404        }
    405
    406        @Override public String readUTF() {
    407          try {
    408            return input.readUTF();
    409          } catch (IOException e) {
    410            throw new IllegalStateException(e);
    411          }
    412        }
    413      }
    414
    415      /**
    416       * Returns a new {@link ByteArrayDataOutput} instance with a default size.
    417       */
    418      public static ByteArrayDataOutput newDataOutput() {
    419        return new ByteArrayDataOutputStream();
    420      }
    421
    422      /**
    423       * Returns a new {@link ByteArrayDataOutput} instance sized to hold
    424       * {@code size} bytes before resizing.
    425       *
    426       * @throws IllegalArgumentException if {@code size} is negative
    427       */
    428      public static ByteArrayDataOutput newDataOutput(int size) {
    429        Preconditions.checkArgument(size >= 0, "Invalid size: %s", size);
    430        return new ByteArrayDataOutputStream(size);
    431      }
    432
    433      @SuppressWarnings("deprecation") // for writeBytes
    434      private static class ByteArrayDataOutputStream
    435          implements ByteArrayDataOutput {
    436
    437        final DataOutput output;
    438        final ByteArrayOutputStream byteArrayOutputSteam;
    439
    440        ByteArrayDataOutputStream() {
    441          this(new ByteArrayOutputStream());
    442        }
    443
    444        ByteArrayDataOutputStream(int size) {
    445          this(new ByteArrayOutputStream(size));
    446        }
    447
    448        ByteArrayDataOutputStream(ByteArrayOutputStream byteArrayOutputSteam) {
    449          this.byteArrayOutputSteam = byteArrayOutputSteam;
    450          output = new DataOutputStream(byteArrayOutputSteam);
    451        }
    452
    453        @Override public void write(int b) {
    454          try {
    455            output.write(b);
    456          } catch (IOException impossible) {
    457            throw new AssertionError(impossible);
    458          }
    459        }
    460
    461        @Override public void write(byte[] b) {
    462          try {
    463            output.write(b);
    464          } catch (IOException impossible) {
    465            throw new AssertionError(impossible);
    466          }
    467        }
    468
    469        @Override public void write(byte[] b, int off, int len) {
    470          try {
    471            output.write(b, off, len);
    472          } catch (IOException impossible) {
    473            throw new AssertionError(impossible);
    474          }
    475        }
    476
    477        @Override public void writeBoolean(boolean v) {
    478          try {
    479            output.writeBoolean(v);
    480          } catch (IOException impossible) {
    481            throw new AssertionError(impossible);
    482          }
    483        }
    484
    485        @Override public void writeByte(int v) {
    486          try {
    487            output.writeByte(v);
    488          } catch (IOException impossible) {
    489            throw new AssertionError(impossible);
    490          }
    491        }
    492
    493        @Override public void writeBytes(String s) {
    494          try {
    495            output.writeBytes(s);
    496          } catch (IOException impossible) {
    497            throw new AssertionError(impossible);
    498          }
    499        }
    500
    501        @Override public void writeChar(int v) {
    502          try {
    503            output.writeChar(v);
    504          } catch (IOException impossible) {
    505            throw new AssertionError(impossible);
    506          }
    507        }
    508
    509        @Override public void writeChars(String s) {
    510          try {
    511            output.writeChars(s);
    512          } catch (IOException impossible) {
    513            throw new AssertionError(impossible);
    514          }
    515        }
    516
    517        @Override public void writeDouble(double v) {
    518          try {
    519            output.writeDouble(v);
    520          } catch (IOException impossible) {
    521            throw new AssertionError(impossible);
    522          }
    523        }
    524
    525        @Override public void writeFloat(float v) {
    526          try {
    527            output.writeFloat(v);
    528          } catch (IOException impossible) {
    529            throw new AssertionError(impossible);
    530          }
    531        }
    532
    533        @Override public void writeInt(int v) {
    534          try {
    535            output.writeInt(v);
    536          } catch (IOException impossible) {
    537            throw new AssertionError(impossible);
    538          }
    539        }
    540
    541        @Override public void writeLong(long v) {
    542          try {
    543            output.writeLong(v);
    544          } catch (IOException impossible) {
    545            throw new AssertionError(impossible);
    546          }
    547        }
    548
    549        @Override public void writeShort(int v) {
    550          try {
    551            output.writeShort(v);
    552          } catch (IOException impossible) {
    553            throw new AssertionError(impossible);
    554          }
    555        }
    556
    557        @Override public void writeUTF(String s) {
    558          try {
    559            output.writeUTF(s);
    560          } catch (IOException impossible) {
    561            throw new AssertionError(impossible);
    562          }
    563        }
    564
    565        @Override public byte[] toByteArray() {
    566          return byteArrayOutputSteam.toByteArray();
    567        }
    568
    569      }
    570
    571      // TODO(chrisn): Not all streams support skipping.
    572      /** Returns the length of a supplied input stream, in bytes. */
    573      public static long length(InputSupplier<? extends InputStream> supplier)
    574          throws IOException {
    575        long count = 0;
    576        boolean threw = true;
    577        InputStream in = supplier.getInput();
    578        try {
    579          while (true) {
    580            // We skip only Integer.MAX_VALUE due to JDK overflow bugs.
    581            long amt = in.skip(Integer.MAX_VALUE);
    582            if (amt == 0) {
    583              if (in.read() == -1) {
    584                threw = false;
    585                return count;
    586              }
    587              count++;
    588            } else {
    589              count += amt;
    590            }
    591          }
    592        } finally {
    593          Closeables.close(in, threw);
    594        }
    595      }
    596
    597      /**
    598       * Returns true if the supplied input streams contain the same bytes.
    599       *
    600       * @throws IOException if an I/O error occurs
    601       */
    602      public static boolean equal(InputSupplier<? extends InputStream> supplier1,
    603          InputSupplier<? extends InputStream> supplier2) throws IOException {
    604        byte[] buf1 = new byte[BUF_SIZE];
    605        byte[] buf2 = new byte[BUF_SIZE];
    606
    607        boolean threw = true;
    608        InputStream in1 = supplier1.getInput();
    609        try {
    610          InputStream in2 = supplier2.getInput();
    611          try {
    612            while (true) {
    613              int read1 = read(in1, buf1, 0, BUF_SIZE);
    614              int read2 = read(in2, buf2, 0, BUF_SIZE);
    615              if (read1 != read2 || !Arrays.equals(buf1, buf2)) {
    616                threw = false;
    617                return false;
    618              } else if (read1 != BUF_SIZE) {
    619                threw = false;
    620                return true;
    621              }
    622            }
    623          } finally {
    624            Closeables.close(in2, threw);
    625          }
    626        } finally {
    627          Closeables.close(in1, threw);
    628        }
    629      }
    630
    631      /**
    632       * Attempts to read enough bytes from the stream to fill the given byte array,
    633       * with the same behavior as {@link DataInput#readFully(byte[])}.
    634       * Does not close the stream.
    635       *
    636       * @param in the input stream to read from.
    637       * @param b the buffer into which the data is read.
    638       * @throws EOFException if this stream reaches the end before reading all
    639       *     the bytes.
    640       * @throws IOException if an I/O error occurs.
    641       */
    642      public static void readFully(InputStream in, byte[] b) throws IOException {
    643        readFully(in, b, 0, b.length);
    644      }
    645
    646      /**
    647       * Attempts to read {@code len} bytes from the stream into the given array
    648       * starting at {@code off}, with the same behavior as
    649       * {@link DataInput#readFully(byte[], int, int)}. Does not close the
    650       * stream.
    651       *
    652       * @param in the input stream to read from.
    653       * @param b the buffer into which the data is read.
    654       * @param off an int specifying the offset into the data.
    655       * @param len an int specifying the number of bytes to read.
    656       * @throws EOFException if this stream reaches the end before reading all
    657       *     the bytes.
    658       * @throws IOException if an I/O error occurs.
    659       */
    660      public static void readFully(InputStream in, byte[] b, int off, int len)
    661          throws IOException {
    662        if (read(in, b, off, len) != len) {
    663          throw new EOFException();
    664        }
    665      }
    666
    667      /**
    668       * Discards {@code n} bytes of data from the input stream. This method
    669       * will block until the full amount has been skipped. Does not close the
    670       * stream.
    671       *
    672       * @param in the input stream to read from
    673       * @param n the number of bytes to skip
    674       * @throws EOFException if this stream reaches the end before skipping all
    675       *     the bytes
    676       * @throws IOException if an I/O error occurs, or the stream does not
    677       *     support skipping
    678       */
    679      public static void skipFully(InputStream in, long n) throws IOException {
    680        while (n > 0) {
    681          long amt = in.skip(n);
    682          if (amt == 0) {
    683            // Force a blocking read to avoid infinite loop
    684            if (in.read() == -1) {
    685              throw new EOFException();
    686            }
    687            n--;
    688          } else {
    689            n -= amt;
    690          }
    691        }
    692      }
    693
    694      /**
    695       * Process the bytes of a supplied stream
    696       *
    697       * @param supplier the input stream factory
    698       * @param processor the object to which to pass the bytes of the stream
    699       * @return the result of the byte processor
    700       * @throws IOException if an I/O error occurs
    701       */
    702      public static <T> T readBytes(InputSupplier<? extends InputStream> supplier,
    703          ByteProcessor<T> processor) throws IOException {
    704        byte[] buf = new byte[BUF_SIZE];
    705        boolean threw = true;
    706        InputStream in = supplier.getInput();
    707        try {
    708          int amt;
    709          do {
    710            amt = in.read(buf);
    711            if (amt == -1) {
    712              threw = false;
    713              break;
    714            }
    715          } while (processor.processBytes(buf, 0, amt));
    716          return processor.getResult();
    717        } finally {
    718          Closeables.close(in, threw);
    719        }
    720      }
    721
    722      /**
    723       * Computes and returns the checksum value for a supplied input stream.
    724       * The checksum object is reset when this method returns successfully.
    725       *
    726       * @param supplier the input stream factory
    727       * @param checksum the checksum object
    728       * @return the result of {@link Checksum#getValue} after updating the
    729       *     checksum object with all of the bytes in the stream
    730       * @throws IOException if an I/O error occurs
    731       */
    732      public static long getChecksum(InputSupplier<? extends InputStream> supplier,
    733          final Checksum checksum) throws IOException {
    734        return readBytes(supplier, new ByteProcessor<Long>() {
    735          @Override
    736          public boolean processBytes(byte[] buf, int off, int len) {
    737            checksum.update(buf, off, len);
    738            return true;
    739          }
    740
    741          @Override
    742          public Long getResult() {
    743            long result = checksum.getValue();
    744            checksum.reset();
    745            return result;
    746          }
    747        });
    748      }
    749
    750      /**
    751       * Computes the hash code of the data supplied by {@code supplier} using {@code
    752       * hashFunction}.
    753       *
    754       * @param supplier the input stream factory
    755       * @param hashFunction the hash function to use to hash the data
    756       * @return the {@link HashCode} of all of the bytes in the input stream
    757       * @throws IOException if an I/O error occurs
    758       * @since 12.0
    759       */
    760      public static HashCode hash(
    761          InputSupplier<? extends InputStream> supplier, HashFunction hashFunction)
    762          throws IOException {
    763        Hasher hasher = hashFunction.newHasher();
    764        copy(supplier, Funnels.asOutputStream(hasher));
    765        return hasher.hash();
    766      }
    767
    768      /**
    769       * Reads some bytes from an input stream and stores them into the buffer array
    770       * {@code b}. This method blocks until {@code len} bytes of input data have
    771       * been read into the array, or end of file is detected. The number of bytes
    772       * read is returned, possibly zero. Does not close the stream.
    773       *
    774       * <p>A caller can detect EOF if the number of bytes read is less than
    775       * {@code len}. All subsequent calls on the same stream will return zero.
    776       *
    777       * <p>If {@code b} is null, a {@code NullPointerException} is thrown. If
    778       * {@code off} is negative, or {@code len} is negative, or {@code off+len} is
    779       * greater than the length of the array {@code b}, then an
    780       * {@code IndexOutOfBoundsException} is thrown. If {@code len} is zero, then
    781       * no bytes are read. Otherwise, the first byte read is stored into element
    782       * {@code b[off]}, the next one into {@code b[off+1]}, and so on. The number
    783       * of bytes read is, at most, equal to {@code len}.
    784       *
    785       * @param in the input stream to read from
    786       * @param b the buffer into which the data is read
    787       * @param off an int specifying the offset into the data
    788       * @param len an int specifying the number of bytes to read
    789       * @return the number of bytes read
    790       * @throws IOException if an I/O error occurs
    791       */
    792      public static int read(InputStream in, byte[] b, int off, int len)
    793          throws IOException {
    794        if (len < 0) {
    795          throw new IndexOutOfBoundsException("len is negative");
    796        }
    797        int total = 0;
    798        while (total < len) {
    799          int result = in.read(b, off + total, len - total);
    800          if (result == -1) {
    801            break;
    802          }
    803          total += result;
    804        }
    805        return total;
    806      }
    807
    808      /**
    809       * Returns an {@link InputSupplier} that returns input streams from the
    810       * an underlying supplier, where each stream starts at the given
    811       * offset and is limited to the specified number of bytes.
    812       *
    813       * @param supplier the supplier from which to get the raw streams
    814       * @param offset the offset in bytes into the underlying stream where
    815       *     the returned streams will start
    816       * @param length the maximum length of the returned streams
    817       * @throws IllegalArgumentException if offset or length are negative
    818       */
    819      public static InputSupplier<InputStream> slice(
    820          final InputSupplier<? extends InputStream> supplier,
    821          final long offset,
    822          final long length) {
    823        Preconditions.checkNotNull(supplier);
    824        Preconditions.checkArgument(offset >= 0, "offset is negative");
    825        Preconditions.checkArgument(length >= 0, "length is negative");
    826        return new InputSupplier<InputStream>() {
    827          @Override public InputStream getInput() throws IOException {
    828            InputStream in = supplier.getInput();
    829            if (offset > 0) {
    830              try {
    831                skipFully(in, offset);
    832              } catch (IOException e) {
    833                Closeables.closeQuietly(in);
    834                throw e;
    835              }
    836            }
    837            return new LimitInputStream(in, length);
    838          }
    839        };
    840      }
    841
    842      /**
    843       * Joins multiple {@link InputStream} suppliers into a single supplier.
    844       * Streams returned from the supplier will contain the concatenated data from
    845       * the streams of the underlying suppliers.
    846       *
    847       * <p>Only one underlying input stream will be open at a time. Closing the
    848       * joined stream will close the open underlying stream.
    849       *
    850       * <p>Reading from the joined stream will throw a {@link NullPointerException}
    851       * if any of the suppliers are null or return null.
    852       *
    853       * @param suppliers the suppliers to concatenate
    854       * @return a supplier that will return a stream containing the concatenated
    855       *     stream data
    856       */
    857      public static InputSupplier<InputStream> join(final
    858          Iterable<? extends InputSupplier<? extends InputStream>> suppliers) {
    859        return new InputSupplier<InputStream>() {
    860          @Override public InputStream getInput() throws IOException {
    861            return new MultiInputStream(suppliers.iterator());
    862          }
    863        };
    864      }
    865
    866      /** Varargs form of {@link #join(Iterable)}. */
    867      public static InputSupplier<InputStream> join(
    868          InputSupplier<? extends InputStream>... suppliers) {
    869        return join(Arrays.asList(suppliers));
    870      }
    871    }