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 }