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