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