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