Coverage Summary for Class: ByteSource (com.google.common.io)

Class Method, % Line, %
ByteSource 10.5% (2/19) 2% (2/98)
ByteSource$AsCharSource 0% (0/5) 0% (0/9)
ByteSource$ByteArrayByteSource 15.4% (2/13) 26.9% (7/26)
ByteSource$ConcatenatedByteSource 0% (0/6) 0% (0/29)
ByteSource$EmptyByteSource 40% (2/5) 42.9% (3/7)
ByteSource$SlicedByteSource 0% (0/8) 0% (0/35)
Total 10.7% (6/56) 5.9% (12/204)


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. */ 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  */ 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  */ 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  */ 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  */ 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  */ 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 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  */ 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. */ 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 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 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  */ 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 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  */ 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  */ 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  */ 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  */ 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  */ 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  */ 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  */ 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  460  AsCharSource(Charset charset) { 461  this.charset = checkNotNull(charset); 462  } 463  464  @Override 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 473  public Reader openStream() throws IOException { 474  return new InputStreamReader(ByteSource.this.openStream(), charset); 475  } 476  477  @Override 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 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  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 509  public InputStream openStream() throws IOException { 510  return sliceStream(ByteSource.this.openStream()); 511  } 512  513  @Override 514  public InputStream openBufferedStream() throws IOException { 515  return sliceStream(ByteSource.this.openBufferedStream()); 516  } 517  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 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 553  public boolean isEmpty() throws IOException { 554  return length == 0 || super.isEmpty(); 555  } 556  557  @Override 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 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  580  ByteArrayByteSource(byte[] bytes) { 581  this(bytes, 0, bytes.length); 582  } 583  584  // NOTE: Preconditions are enforced by slice, the only non-trivial caller. 585  ByteArrayByteSource(byte[] bytes, int offset, int length) { 586  this.bytes = bytes; 587  this.offset = offset; 588  this.length = length; 589  } 590  591  @Override 592  public InputStream openStream() { 593  return new ByteArrayInputStream(bytes, offset, length); 594  } 595  596  @Override 597  public InputStream openBufferedStream() throws IOException { 598  return openStream(); 599  } 600  601  @Override 602  public boolean isEmpty() { 603  return length == 0; 604  } 605  606  @Override 607  public long size() { 608  return length; 609  } 610  611  @Override 612  public Optional<Long> sizeIfKnown() { 613  return Optional.of((long) length); 614  } 615  616  @Override 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 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 630  public long copyTo(OutputStream output) throws IOException { 631  output.write(bytes, offset, length); 632  return length; 633  } 634  635  @Override 636  public HashCode hash(HashFunction hashFunction) throws IOException { 637  return hashFunction.hashBytes(bytes, offset, length); 638  } 639  640  @Override 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 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  663  EmptyByteSource() { 664  super(new byte[0]); 665  } 666  667  @Override 668  public CharSource asCharSource(Charset charset) { 669  checkNotNull(charset); 670  return CharSource.empty(); 671  } 672  673  @Override 674  public byte[] read() { 675  return bytes; // length is 0, no need to clone 676  } 677  678  @Override 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  688  ConcatenatedByteSource(Iterable<? extends ByteSource> sources) { 689  this.sources = checkNotNull(sources); 690  } 691  692  @Override 693  public InputStream openStream() throws IOException { 694  return new MultiInputStream(sources.iterator()); 695  } 696  697  @Override 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 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 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 754  public String toString() { 755  return "ByteSource.concat(" + sources + ")"; 756  } 757  } 758 }