• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.io;
16 
17 import static com.google.common.base.Preconditions.checkArgument;
18 import static com.google.common.base.Preconditions.checkNotNull;
19 import static com.google.common.io.ByteStreams.createBuffer;
20 import static com.google.common.io.ByteStreams.skipUpTo;
21 
22 import com.google.common.annotations.Beta;
23 import com.google.common.annotations.GwtIncompatible;
24 import com.google.common.base.Ascii;
25 import com.google.common.base.Optional;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.hash.Funnels;
28 import com.google.common.hash.HashCode;
29 import com.google.common.hash.HashFunction;
30 import com.google.common.hash.Hasher;
31 import com.google.errorprone.annotations.CanIgnoreReturnValue;
32 import java.io.BufferedInputStream;
33 import java.io.ByteArrayInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.InputStreamReader;
37 import java.io.OutputStream;
38 import java.io.Reader;
39 import java.nio.charset.Charset;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Iterator;
43 import org.checkerframework.checker.nullness.qual.Nullable;
44 
45 /**
46  * A readable source of bytes, such as a file. Unlike an {@link InputStream}, a {@code ByteSource}
47  * is not an open, stateful stream for input that can be read and closed. Instead, it is an
48  * immutable <i>supplier</i> of {@code InputStream} instances.
49  *
50  * <p>{@code ByteSource} provides two kinds of methods:
51  *
52  * <ul>
53  *   <li><b>Methods that return a stream:</b> These methods should return a <i>new</i>, independent
54  *       instance each time they are called. The caller is responsible for ensuring that the
55  *       returned stream is closed.
56  *   <li><b>Convenience methods:</b> These are implementations of common operations that are
57  *       typically implemented by opening a stream using one of the methods in the first category,
58  *       doing something and finally closing the stream that was opened.
59  * </ul>
60  *
61  * <p><b>Note:</b> In general, {@code ByteSource} is intended to be used for "file-like" sources
62  * that provide streams that are:
63  *
64  * <ul>
65  *   <li><b>Finite:</b> Many operations, such as {@link #size()} and {@link #read()}, will either
66  *       block indefinitely or fail if the source creates an infinite stream.
67  *   <li><b>Non-destructive:</b> A <i>destructive</i> stream will consume or otherwise alter the
68  *       bytes of the source as they are read from it. A source that provides such streams will not
69  *       be reusable, and operations that read from the stream (including {@link #size()}, in some
70  *       implementations) will prevent further operations from completing as expected.
71  * </ul>
72  *
73  * @since 14.0
74  * @author Colin Decker
75  */
76 @GwtIncompatible
77 @ElementTypesAreNonnullByDefault
78 public abstract class ByteSource {
79 
80   /** Constructor for use by subclasses. */
ByteSource()81   protected ByteSource() {}
82 
83   /**
84    * Returns a {@link CharSource} view of this byte source that decodes bytes read from this source
85    * as characters using the given {@link Charset}.
86    *
87    * <p>If {@link CharSource#asByteSource} is called on the returned source with the same charset,
88    * the default implementation of this method will ensure that the original {@code ByteSource} is
89    * returned, rather than round-trip encoding. Subclasses that override this method should behave
90    * the same way.
91    */
asCharSource(Charset charset)92   public CharSource asCharSource(Charset charset) {
93     return new AsCharSource(charset);
94   }
95 
96   /**
97    * Opens a new {@link InputStream} for reading from this source. This method returns a new,
98    * independent stream each time it is called.
99    *
100    * <p>The caller is responsible for ensuring that the returned stream is closed.
101    *
102    * @throws IOException if an I/O error occurs while opening the stream
103    */
openStream()104   public abstract InputStream openStream() throws IOException;
105 
106   /**
107    * Opens a new buffered {@link InputStream} for reading from this source. The returned stream is
108    * not required to be a {@link BufferedInputStream} in order to allow implementations to simply
109    * delegate to {@link #openStream()} when the stream returned by that method does not benefit from
110    * additional buffering (for example, a {@code ByteArrayInputStream}). This method returns a new,
111    * independent stream each time it is called.
112    *
113    * <p>The caller is responsible for ensuring that the returned stream is closed.
114    *
115    * @throws IOException if an I/O error occurs while opening the stream
116    * @since 15.0 (in 14.0 with return type {@link BufferedInputStream})
117    */
openBufferedStream()118   public InputStream openBufferedStream() throws IOException {
119     InputStream in = openStream();
120     return (in instanceof BufferedInputStream)
121         ? (BufferedInputStream) in
122         : new BufferedInputStream(in);
123   }
124 
125   /**
126    * Returns a view of a slice of this byte source that is at most {@code length} bytes long
127    * starting at the given {@code offset}. If {@code offset} is greater than the size of this
128    * source, the returned source will be empty. If {@code offset + length} is greater than the size
129    * of this source, the returned source will contain the slice starting at {@code offset} and
130    * ending at the end of this source.
131    *
132    * @throws IllegalArgumentException if {@code offset} or {@code length} is negative
133    */
slice(long offset, long length)134   public ByteSource slice(long offset, long length) {
135     return new SlicedByteSource(offset, length);
136   }
137 
138   /**
139    * Returns whether the source has zero bytes. The default implementation first checks {@link
140    * #sizeIfKnown}, returning true if it's known to be zero and false if it's known to be non-zero.
141    * If the size is not known, it falls back to opening a stream and checking for EOF.
142    *
143    * <p>Note that, in cases where {@code sizeIfKnown} returns zero, it is <i>possible</i> that bytes
144    * are actually available for reading. (For example, some special files may return a size of 0
145    * despite actually having content when read.) This means that a source may return {@code true}
146    * from {@code isEmpty()} despite having readable content.
147    *
148    * @throws IOException if an I/O error occurs
149    * @since 15.0
150    */
isEmpty()151   public boolean isEmpty() throws IOException {
152     Optional<Long> sizeIfKnown = sizeIfKnown();
153     if (sizeIfKnown.isPresent()) {
154       return sizeIfKnown.get() == 0L;
155     }
156     Closer closer = Closer.create();
157     try {
158       InputStream in = closer.register(openStream());
159       return in.read() == -1;
160     } catch (Throwable e) {
161       throw closer.rethrow(e);
162     } finally {
163       closer.close();
164     }
165   }
166 
167   /**
168    * Returns the size of this source in bytes, if the size can be easily determined without actually
169    * opening the data stream.
170    *
171    * <p>The default implementation returns {@link Optional#absent}. Some sources, such as a file,
172    * may return a non-absent value. Note that in such cases, it is <i>possible</i> that this method
173    * will return a different number of bytes than would be returned by reading all of the bytes (for
174    * example, some special files may return a size of 0 despite actually having content when read).
175    *
176    * <p>Additionally, for mutable sources such as files, a subsequent read may return a different
177    * number of bytes if the contents are changed.
178    *
179    * @since 19.0
180    */
181   @Beta
sizeIfKnown()182   public Optional<Long> sizeIfKnown() {
183     return Optional.absent();
184   }
185 
186   /**
187    * Returns the size of this source in bytes, even if doing so requires opening and traversing an
188    * entire stream. To avoid a potentially expensive operation, see {@link #sizeIfKnown}.
189    *
190    * <p>The default implementation calls {@link #sizeIfKnown} and returns the value if present. If
191    * absent, it will fall back to a heavyweight operation that will open a stream, read (or {@link
192    * InputStream#skip(long) skip}, if possible) to the end of the stream and return the total number
193    * of bytes that were read.
194    *
195    * <p>Note that for some sources that implement {@link #sizeIfKnown} to provide a more efficient
196    * implementation, it is <i>possible</i> that this method will return a different number of bytes
197    * than would be returned by reading all of the bytes (for example, some special files may return
198    * a size of 0 despite actually having content when read).
199    *
200    * <p>In either case, for mutable sources such as files, a subsequent read may return a different
201    * number of bytes if the contents are changed.
202    *
203    * @throws IOException if an I/O error occurs while reading the size of this source
204    */
size()205   public long size() throws IOException {
206     Optional<Long> sizeIfKnown = sizeIfKnown();
207     if (sizeIfKnown.isPresent()) {
208       return sizeIfKnown.get();
209     }
210 
211     Closer closer = Closer.create();
212     try {
213       InputStream in = closer.register(openStream());
214       return countBySkipping(in);
215     } catch (IOException e) {
216       // skip may not be supported... at any rate, try reading
217     } finally {
218       closer.close();
219     }
220 
221     closer = Closer.create();
222     try {
223       InputStream in = closer.register(openStream());
224       return ByteStreams.exhaust(in);
225     } catch (Throwable e) {
226       throw closer.rethrow(e);
227     } finally {
228       closer.close();
229     }
230   }
231 
232   /** Counts the bytes in the given input stream using skip if possible. */
countBySkipping(InputStream in)233   private long countBySkipping(InputStream in) throws IOException {
234     long count = 0;
235     long skipped;
236     while ((skipped = skipUpTo(in, Integer.MAX_VALUE)) > 0) {
237       count += skipped;
238     }
239     return count;
240   }
241 
242   /**
243    * Copies the contents of this byte source to the given {@code OutputStream}. Does not close
244    * {@code output}.
245    *
246    * @return the number of bytes copied
247    * @throws IOException if an I/O error occurs while reading from this source or writing to {@code
248    *     output}
249    */
250   @CanIgnoreReturnValue
copyTo(OutputStream output)251   public long copyTo(OutputStream output) throws IOException {
252     checkNotNull(output);
253 
254     Closer closer = Closer.create();
255     try {
256       InputStream in = closer.register(openStream());
257       return ByteStreams.copy(in, output);
258     } catch (Throwable e) {
259       throw closer.rethrow(e);
260     } finally {
261       closer.close();
262     }
263   }
264 
265   /**
266    * Copies the contents of this byte source to the given {@code ByteSink}.
267    *
268    * @return the number of bytes copied
269    * @throws IOException if an I/O error occurs while reading from this source or writing to {@code
270    *     sink}
271    */
272   @CanIgnoreReturnValue
copyTo(ByteSink sink)273   public long copyTo(ByteSink sink) throws IOException {
274     checkNotNull(sink);
275 
276     Closer closer = Closer.create();
277     try {
278       InputStream in = closer.register(openStream());
279       OutputStream out = closer.register(sink.openStream());
280       return ByteStreams.copy(in, out);
281     } catch (Throwable e) {
282       throw closer.rethrow(e);
283     } finally {
284       closer.close();
285     }
286   }
287 
288   /**
289    * Reads the full contents of this byte source as a byte array.
290    *
291    * @throws IOException if an I/O error occurs while reading from this source
292    */
read()293   public byte[] read() throws IOException {
294     Closer closer = Closer.create();
295     try {
296       InputStream in = closer.register(openStream());
297       Optional<Long> size = sizeIfKnown();
298       return size.isPresent()
299           ? ByteStreams.toByteArray(in, size.get())
300           : ByteStreams.toByteArray(in);
301     } catch (Throwable e) {
302       throw closer.rethrow(e);
303     } finally {
304       closer.close();
305     }
306   }
307 
308   /**
309    * Reads the contents of this byte source using the given {@code processor} to process bytes as
310    * they are read. Stops when all bytes have been read or the consumer returns {@code false}.
311    * Returns the result produced by the processor.
312    *
313    * @throws IOException if an I/O error occurs while reading from this source or if {@code
314    *     processor} throws an {@code IOException}
315    * @since 16.0
316    */
317   @Beta
318   @CanIgnoreReturnValue // some processors won't return a useful result
read(ByteProcessor<T> processor)319   public <T extends @Nullable Object> T read(ByteProcessor<T> processor) throws IOException {
320     checkNotNull(processor);
321 
322     Closer closer = Closer.create();
323     try {
324       InputStream in = closer.register(openStream());
325       return ByteStreams.readBytes(in, processor);
326     } catch (Throwable e) {
327       throw closer.rethrow(e);
328     } finally {
329       closer.close();
330     }
331   }
332 
333   /**
334    * Hashes the contents of this byte source using the given hash function.
335    *
336    * @throws IOException if an I/O error occurs while reading from this source
337    */
hash(HashFunction hashFunction)338   public HashCode hash(HashFunction hashFunction) throws IOException {
339     Hasher hasher = hashFunction.newHasher();
340     copyTo(Funnels.asOutputStream(hasher));
341     return hasher.hash();
342   }
343 
344   /**
345    * Checks that the contents of this byte source are equal to the contents of the given byte
346    * source.
347    *
348    * @throws IOException if an I/O error occurs while reading from this source or {@code other}
349    */
contentEquals(ByteSource other)350   public boolean contentEquals(ByteSource other) throws IOException {
351     checkNotNull(other);
352 
353     byte[] buf1 = createBuffer();
354     byte[] buf2 = createBuffer();
355 
356     Closer closer = Closer.create();
357     try {
358       InputStream in1 = closer.register(openStream());
359       InputStream in2 = closer.register(other.openStream());
360       while (true) {
361         int read1 = ByteStreams.read(in1, buf1, 0, buf1.length);
362         int read2 = ByteStreams.read(in2, buf2, 0, buf2.length);
363         if (read1 != read2 || !Arrays.equals(buf1, buf2)) {
364           return false;
365         } else if (read1 != buf1.length) {
366           return true;
367         }
368       }
369     } catch (Throwable e) {
370       throw closer.rethrow(e);
371     } finally {
372       closer.close();
373     }
374   }
375 
376   /**
377    * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from
378    * the source will contain the concatenated data from the streams of the underlying sources.
379    *
380    * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will
381    * close the open underlying stream.
382    *
383    * @param sources the sources to concatenate
384    * @return a {@code ByteSource} containing the concatenated data
385    * @since 15.0
386    */
concat(Iterable<? extends ByteSource> sources)387   public static ByteSource concat(Iterable<? extends ByteSource> sources) {
388     return new ConcatenatedByteSource(sources);
389   }
390 
391   /**
392    * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from
393    * the source will contain the concatenated data from the streams of the underlying sources.
394    *
395    * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will
396    * close the open underlying stream.
397    *
398    * <p>Note: The input {@code Iterator} will be copied to an {@code ImmutableList} when this method
399    * is called. This will fail if the iterator is infinite and may cause problems if the iterator
400    * eagerly fetches data for each source when iterated (rather than producing sources that only
401    * load data through their streams). Prefer using the {@link #concat(Iterable)} overload if
402    * possible.
403    *
404    * @param sources the sources to concatenate
405    * @return a {@code ByteSource} containing the concatenated data
406    * @throws NullPointerException if any of {@code sources} is {@code null}
407    * @since 15.0
408    */
concat(Iterator<? extends ByteSource> sources)409   public static ByteSource concat(Iterator<? extends ByteSource> sources) {
410     return concat(ImmutableList.copyOf(sources));
411   }
412 
413   /**
414    * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from
415    * the source will contain the concatenated data from the streams of the underlying sources.
416    *
417    * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will
418    * close the open underlying stream.
419    *
420    * @param sources the sources to concatenate
421    * @return a {@code ByteSource} containing the concatenated data
422    * @throws NullPointerException if any of {@code sources} is {@code null}
423    * @since 15.0
424    */
concat(ByteSource... sources)425   public static ByteSource concat(ByteSource... sources) {
426     return concat(ImmutableList.copyOf(sources));
427   }
428 
429   /**
430    * Returns a view of the given byte array as a {@link ByteSource}. To view only a specific range
431    * in the array, use {@code ByteSource.wrap(b).slice(offset, length)}.
432    *
433    * <p>Note that the given byte array may be passed directly to methods on, for example, {@code
434    * OutputStream} (when {@code copyTo(OutputStream)} is called on the resulting {@code
435    * ByteSource}). This could allow a malicious {@code OutputStream} implementation to modify the
436    * contents of the array, but provides better performance in the normal case.
437    *
438    * @since 15.0 (since 14.0 as {@code ByteStreams.asByteSource(byte[])}).
439    */
wrap(byte[] b)440   public static ByteSource wrap(byte[] b) {
441     return new ByteArrayByteSource(b);
442   }
443 
444   /**
445    * Returns an immutable {@link ByteSource} that contains no bytes.
446    *
447    * @since 15.0
448    */
empty()449   public static ByteSource empty() {
450     return EmptyByteSource.INSTANCE;
451   }
452 
453   /**
454    * A char source that reads bytes from this source and decodes them as characters using a charset.
455    */
456   class AsCharSource extends CharSource {
457 
458     final Charset charset;
459 
AsCharSource(Charset charset)460     AsCharSource(Charset charset) {
461       this.charset = checkNotNull(charset);
462     }
463 
464     @Override
asByteSource(Charset charset)465     public ByteSource asByteSource(Charset charset) {
466       if (charset.equals(this.charset)) {
467         return ByteSource.this;
468       }
469       return super.asByteSource(charset);
470     }
471 
472     @Override
openStream()473     public Reader openStream() throws IOException {
474       return new InputStreamReader(ByteSource.this.openStream(), charset);
475     }
476 
477     @Override
read()478     public String read() throws IOException {
479       // Reading all the data as a byte array is more efficient than the default read()
480       // implementation because:
481       // 1. the string constructor can avoid an extra copy most of the time by correctly sizing the
482       //    internal char array (hard to avoid using StringBuilder)
483       // 2. we avoid extra copies into temporary buffers altogether
484       // The downside is that this will cause us to store the file bytes in memory twice for a short
485       // amount of time.
486       return new String(ByteSource.this.read(), charset);
487     }
488 
489     @Override
toString()490     public String toString() {
491       return ByteSource.this.toString() + ".asCharSource(" + charset + ")";
492     }
493   }
494 
495   /** A view of a subsection of the containing byte source. */
496   private final class SlicedByteSource extends ByteSource {
497 
498     final long offset;
499     final long length;
500 
SlicedByteSource(long offset, long length)501     SlicedByteSource(long offset, long length) {
502       checkArgument(offset >= 0, "offset (%s) may not be negative", offset);
503       checkArgument(length >= 0, "length (%s) may not be negative", length);
504       this.offset = offset;
505       this.length = length;
506     }
507 
508     @Override
openStream()509     public InputStream openStream() throws IOException {
510       return sliceStream(ByteSource.this.openStream());
511     }
512 
513     @Override
openBufferedStream()514     public InputStream openBufferedStream() throws IOException {
515       return sliceStream(ByteSource.this.openBufferedStream());
516     }
517 
sliceStream(InputStream in)518     private InputStream sliceStream(InputStream in) throws IOException {
519       if (offset > 0) {
520         long skipped;
521         try {
522           skipped = ByteStreams.skipUpTo(in, offset);
523         } catch (Throwable e) {
524           Closer closer = Closer.create();
525           closer.register(in);
526           try {
527             throw closer.rethrow(e);
528           } finally {
529             closer.close();
530           }
531         }
532 
533         if (skipped < offset) {
534           // offset was beyond EOF
535           in.close();
536           return new ByteArrayInputStream(new byte[0]);
537         }
538       }
539       return ByteStreams.limit(in, length);
540     }
541 
542     @Override
slice(long offset, long length)543     public ByteSource slice(long offset, long length) {
544       checkArgument(offset >= 0, "offset (%s) may not be negative", offset);
545       checkArgument(length >= 0, "length (%s) may not be negative", length);
546       long maxLength = this.length - offset;
547       return maxLength <= 0
548           ? ByteSource.empty()
549           : ByteSource.this.slice(this.offset + offset, Math.min(length, maxLength));
550     }
551 
552     @Override
isEmpty()553     public boolean isEmpty() throws IOException {
554       return length == 0 || super.isEmpty();
555     }
556 
557     @Override
sizeIfKnown()558     public Optional<Long> sizeIfKnown() {
559       Optional<Long> optionalUnslicedSize = ByteSource.this.sizeIfKnown();
560       if (optionalUnslicedSize.isPresent()) {
561         long unslicedSize = optionalUnslicedSize.get();
562         long off = Math.min(offset, unslicedSize);
563         return Optional.of(Math.min(length, unslicedSize - off));
564       }
565       return Optional.absent();
566     }
567 
568     @Override
toString()569     public String toString() {
570       return ByteSource.this.toString() + ".slice(" + offset + ", " + length + ")";
571     }
572   }
573 
574   private static class ByteArrayByteSource extends ByteSource {
575 
576     final byte[] bytes;
577     final int offset;
578     final int length;
579 
ByteArrayByteSource(byte[] bytes)580     ByteArrayByteSource(byte[] bytes) {
581       this(bytes, 0, bytes.length);
582     }
583 
584     // NOTE: Preconditions are enforced by slice, the only non-trivial caller.
ByteArrayByteSource(byte[] bytes, int offset, int length)585     ByteArrayByteSource(byte[] bytes, int offset, int length) {
586       this.bytes = bytes;
587       this.offset = offset;
588       this.length = length;
589     }
590 
591     @Override
openStream()592     public InputStream openStream() {
593       return new ByteArrayInputStream(bytes, offset, length);
594     }
595 
596     @Override
openBufferedStream()597     public InputStream openBufferedStream() throws IOException {
598       return openStream();
599     }
600 
601     @Override
isEmpty()602     public boolean isEmpty() {
603       return length == 0;
604     }
605 
606     @Override
size()607     public long size() {
608       return length;
609     }
610 
611     @Override
sizeIfKnown()612     public Optional<Long> sizeIfKnown() {
613       return Optional.of((long) length);
614     }
615 
616     @Override
read()617     public byte[] read() {
618       return Arrays.copyOfRange(bytes, offset, offset + length);
619     }
620 
621     @SuppressWarnings("CheckReturnValue") // it doesn't matter what processBytes returns here
622     @Override
623     @ParametricNullness
read(ByteProcessor<T> processor)624     public <T extends @Nullable Object> T read(ByteProcessor<T> processor) throws IOException {
625       processor.processBytes(bytes, offset, length);
626       return processor.getResult();
627     }
628 
629     @Override
copyTo(OutputStream output)630     public long copyTo(OutputStream output) throws IOException {
631       output.write(bytes, offset, length);
632       return length;
633     }
634 
635     @Override
hash(HashFunction hashFunction)636     public HashCode hash(HashFunction hashFunction) throws IOException {
637       return hashFunction.hashBytes(bytes, offset, length);
638     }
639 
640     @Override
slice(long offset, long length)641     public ByteSource slice(long offset, long length) {
642       checkArgument(offset >= 0, "offset (%s) may not be negative", offset);
643       checkArgument(length >= 0, "length (%s) may not be negative", length);
644 
645       offset = Math.min(offset, this.length);
646       length = Math.min(length, this.length - offset);
647       int newOffset = this.offset + (int) offset;
648       return new ByteArrayByteSource(bytes, newOffset, (int) length);
649     }
650 
651     @Override
toString()652     public String toString() {
653       return "ByteSource.wrap("
654           + Ascii.truncate(BaseEncoding.base16().encode(bytes, offset, length), 30, "...")
655           + ")";
656     }
657   }
658 
659   private static final class EmptyByteSource extends ByteArrayByteSource {
660 
661     static final EmptyByteSource INSTANCE = new EmptyByteSource();
662 
EmptyByteSource()663     EmptyByteSource() {
664       super(new byte[0]);
665     }
666 
667     @Override
asCharSource(Charset charset)668     public CharSource asCharSource(Charset charset) {
669       checkNotNull(charset);
670       return CharSource.empty();
671     }
672 
673     @Override
read()674     public byte[] read() {
675       return bytes; // length is 0, no need to clone
676     }
677 
678     @Override
toString()679     public String toString() {
680       return "ByteSource.empty()";
681     }
682   }
683 
684   private static final class ConcatenatedByteSource extends ByteSource {
685 
686     final Iterable<? extends ByteSource> sources;
687 
ConcatenatedByteSource(Iterable<? extends ByteSource> sources)688     ConcatenatedByteSource(Iterable<? extends ByteSource> sources) {
689       this.sources = checkNotNull(sources);
690     }
691 
692     @Override
openStream()693     public InputStream openStream() throws IOException {
694       return new MultiInputStream(sources.iterator());
695     }
696 
697     @Override
isEmpty()698     public boolean isEmpty() throws IOException {
699       for (ByteSource source : sources) {
700         if (!source.isEmpty()) {
701           return false;
702         }
703       }
704       return true;
705     }
706 
707     @Override
sizeIfKnown()708     public Optional<Long> sizeIfKnown() {
709       if (!(sources instanceof Collection)) {
710         // Infinite Iterables can cause problems here. Of course, it's true that most of the other
711         // methods on this class also have potential problems with infinite  Iterables. But unlike
712         // those, this method can cause issues even if the user is dealing with a (finite) slice()
713         // of this source, since the slice's sizeIfKnown() method needs to know the size of the
714         // underlying source to know what its size actually is.
715         return Optional.absent();
716       }
717       long result = 0L;
718       for (ByteSource source : sources) {
719         Optional<Long> sizeIfKnown = source.sizeIfKnown();
720         if (!sizeIfKnown.isPresent()) {
721           return Optional.absent();
722         }
723         result += sizeIfKnown.get();
724         if (result < 0) {
725           // Overflow (or one or more sources that returned a negative size, but all bets are off in
726           // that case)
727           // Can't represent anything higher, and realistically there probably isn't anything that
728           // can actually be done anyway with the supposed 8+ exbibytes of data the source is
729           // claiming to have if we get here, so just stop.
730           return Optional.of(Long.MAX_VALUE);
731         }
732       }
733       return Optional.of(result);
734     }
735 
736     @Override
size()737     public long size() throws IOException {
738       long result = 0L;
739       for (ByteSource source : sources) {
740         result += source.size();
741         if (result < 0) {
742           // Overflow (or one or more sources that returned a negative size, but all bets are off in
743           // that case)
744           // Can't represent anything higher, and realistically there probably isn't anything that
745           // can actually be done anyway with the supposed 8+ exbibytes of data the source is
746           // claiming to have if we get here, so just stop.
747           return Long.MAX_VALUE;
748         }
749       }
750       return result;
751     }
752 
753     @Override
toString()754     public String toString() {
755       return "ByteSource.concat(" + sources + ")";
756     }
757   }
758 }
759