1/* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.common.io; 18 19import static com.google.common.base.Preconditions.checkArgument; 20import static com.google.common.base.Preconditions.checkNotNull; 21 22import com.google.common.annotations.Beta; 23import com.google.common.base.Ascii; 24import com.google.common.collect.ImmutableList; 25import com.google.common.hash.Funnels; 26import com.google.common.hash.HashCode; 27import com.google.common.hash.HashFunction; 28import com.google.common.hash.Hasher; 29 30import java.io.BufferedInputStream; 31import java.io.ByteArrayInputStream; 32import java.io.IOException; 33import java.io.InputStream; 34import java.io.InputStreamReader; 35import java.io.OutputStream; 36import java.io.Reader; 37import java.nio.charset.Charset; 38import java.util.Arrays; 39import java.util.Iterator; 40 41/** 42 * A readable source of bytes, such as a file. Unlike an {@link InputStream}, a 43 * {@code ByteSource} is not an open, stateful stream for input that can be read and closed. 44 * Instead, it is an immutable <i>supplier</i> of {@code InputStream} instances. 45 * 46 * <p>{@code ByteSource} provides two kinds of methods: 47 * <ul> 48 * <li><b>Methods that return a stream:</b> These methods should return a <i>new</i>, independent 49 * instance each time they are called. The caller is responsible for ensuring that the returned 50 * stream is closed. 51 * <li><b>Convenience methods:</b> These are implementations of common operations that are 52 * typically implemented by opening a stream using one of the methods in the first category, doing 53 * something and finally closing the stream that was opened. 54 * </ul> 55 * 56 * @since 14.0 57 * @author Colin Decker 58 */ 59public abstract class ByteSource implements InputSupplier<InputStream> { 60 61 private static final int BUF_SIZE = 0x1000; // 4K 62 63 /** 64 * Constructor for use by subclasses. 65 */ 66 protected ByteSource() {} 67 68 /** 69 * Returns a {@link CharSource} view of this byte source that decodes bytes read from this source 70 * as characters using the given {@link Charset}. 71 */ 72 public CharSource asCharSource(Charset charset) { 73 return new AsCharSource(charset); 74 } 75 76 /** 77 * Opens a new {@link InputStream} for reading from this source. This method should return a new, 78 * independent stream each time it is called. 79 * 80 * <p>The caller is responsible for ensuring that the returned stream is closed. 81 * 82 * @throws IOException if an I/O error occurs in the process of opening the stream 83 */ 84 public abstract InputStream openStream() throws IOException; 85 86 /** 87 * This method is a temporary method provided for easing migration from suppliers to sources and 88 * sinks. 89 * 90 * @since 15.0 91 * @deprecated This method is only provided for temporary compatibility with the 92 * {@link InputSupplier} interface and should not be called directly. Use {@link #openStream} 93 * instead. This method is scheduled for removal in Guava 18.0. 94 */ 95 @Override 96 @Deprecated 97 public final InputStream getInput() throws IOException { 98 return openStream(); 99 } 100 101 /** 102 * Opens a new buffered {@link InputStream} for reading from this source. The returned stream is 103 * not required to be a {@link BufferedInputStream} in order to allow implementations to simply 104 * delegate to {@link #openStream()} when the stream returned by that method does not benefit 105 * from additional buffering (for example, a {@code ByteArrayInputStream}). This method should 106 * return a new, independent stream each time it is called. 107 * 108 * <p>The caller is responsible for ensuring that the returned stream is closed. 109 * 110 * @throws IOException if an I/O error occurs in the process of opening the stream 111 * @since 15.0 (in 14.0 with return type {@link BufferedInputStream}) 112 */ 113 public InputStream openBufferedStream() throws IOException { 114 InputStream in = openStream(); 115 return (in instanceof BufferedInputStream) 116 ? (BufferedInputStream) in 117 : new BufferedInputStream(in); 118 } 119 120 /** 121 * Returns a view of a slice of this byte source that is at most {@code length} bytes long 122 * starting at the given {@code offset}. 123 * 124 * @throws IllegalArgumentException if {@code offset} or {@code length} is negative 125 */ 126 public ByteSource slice(long offset, long length) { 127 return new SlicedByteSource(offset, length); 128 } 129 130 /** 131 * Returns whether the source has zero bytes. The default implementation is to open a stream and 132 * check for EOF. 133 * 134 * @throws IOException if an I/O error occurs 135 * @since 15.0 136 */ 137 public boolean isEmpty() throws IOException { 138 Closer closer = Closer.create(); 139 try { 140 InputStream in = closer.register(openStream()); 141 return in.read() == -1; 142 } catch (Throwable e) { 143 throw closer.rethrow(e); 144 } finally { 145 closer.close(); 146 } 147 } 148 149 /** 150 * Returns the size of this source in bytes. For most implementations, this is a heavyweight 151 * operation that will open a stream, read (or {@link InputStream#skip(long) skip}, if possible) 152 * to the end of the stream and return the total number of bytes that were read. 153 * 154 * <p>For some sources, such as a file, this method may use a more efficient implementation. Note 155 * that in such cases, it is <i>possible</i> that this method will return a different number of 156 * bytes than would be returned by reading all of the bytes (for example, some special files may 157 * return a size of 0 despite actually having content when read). 158 * 159 * <p>In either case, if this is a mutable source such as a file, the size it returns may not be 160 * the same number of bytes a subsequent read would return. 161 * 162 * @throws IOException if an I/O error occurs in the process of reading the size of this source 163 */ 164 public long size() throws IOException { 165 Closer closer = Closer.create(); 166 try { 167 InputStream in = closer.register(openStream()); 168 return countBySkipping(in); 169 } catch (IOException e) { 170 // skip may not be supported... at any rate, try reading 171 } finally { 172 closer.close(); 173 } 174 175 closer = Closer.create(); 176 try { 177 InputStream in = closer.register(openStream()); 178 return countByReading(in); 179 } catch (Throwable e) { 180 throw closer.rethrow(e); 181 } finally { 182 closer.close(); 183 } 184 } 185 186 /** 187 * Counts the bytes in the given input stream using skip if possible. Returns SKIP_FAILED if the 188 * first call to skip threw, in which case skip may just not be supported. 189 */ 190 private long countBySkipping(InputStream in) throws IOException { 191 long count = 0; 192 while (true) { 193 // don't try to skip more than available() 194 // things may work really wrong with FileInputStream otherwise 195 long skipped = in.skip(Math.min(in.available(), Integer.MAX_VALUE)); 196 if (skipped <= 0) { 197 if (in.read() == -1) { 198 return count; 199 } else if (count == 0 && in.available() == 0) { 200 // if available is still zero after reading a single byte, it 201 // will probably always be zero, so we should countByReading 202 throw new IOException(); 203 } 204 count++; 205 } else { 206 count += skipped; 207 } 208 } 209 } 210 211 private static final byte[] countBuffer = new byte[BUF_SIZE]; 212 213 private long countByReading(InputStream in) throws IOException { 214 long count = 0; 215 long read; 216 while ((read = in.read(countBuffer)) != -1) { 217 count += read; 218 } 219 return count; 220 } 221 222 /** 223 * Copies the contents of this byte source to the given {@code OutputStream}. Does not close 224 * {@code output}. 225 * 226 * @throws IOException if an I/O error occurs in the process of reading from this source or 227 * writing to {@code output} 228 */ 229 public long copyTo(OutputStream output) throws IOException { 230 checkNotNull(output); 231 232 Closer closer = Closer.create(); 233 try { 234 InputStream in = closer.register(openStream()); 235 return ByteStreams.copy(in, output); 236 } catch (Throwable e) { 237 throw closer.rethrow(e); 238 } finally { 239 closer.close(); 240 } 241 } 242 243 /** 244 * Copies the contents of this byte source to the given {@code ByteSink}. 245 * 246 * @throws IOException if an I/O error occurs in the process of reading from this source or 247 * writing to {@code sink} 248 */ 249 public long copyTo(ByteSink sink) throws IOException { 250 checkNotNull(sink); 251 252 Closer closer = Closer.create(); 253 try { 254 InputStream in = closer.register(openStream()); 255 OutputStream out = closer.register(sink.openStream()); 256 return ByteStreams.copy(in, out); 257 } catch (Throwable e) { 258 throw closer.rethrow(e); 259 } finally { 260 closer.close(); 261 } 262 } 263 264 /** 265 * Reads the full contents of this byte source as a byte array. 266 * 267 * @throws IOException if an I/O error occurs in the process of reading from this source 268 */ 269 public byte[] read() throws IOException { 270 Closer closer = Closer.create(); 271 try { 272 InputStream in = closer.register(openStream()); 273 return ByteStreams.toByteArray(in); 274 } catch (Throwable e) { 275 throw closer.rethrow(e); 276 } finally { 277 closer.close(); 278 } 279 } 280 281 /** 282 * Reads the contents of this byte source using the given {@code processor} to process bytes as 283 * they are read. Stops when all bytes have been read or the consumer returns {@code false}. 284 * Returns the result produced by the processor. 285 * 286 * @throws IOException if an I/O error occurs in the process of reading from this source or if 287 * {@code processor} throws an {@code IOException} 288 * @since 16.0 289 */ 290 @Beta 291 public <T> T read(ByteProcessor<T> processor) throws IOException { 292 checkNotNull(processor); 293 294 Closer closer = Closer.create(); 295 try { 296 InputStream in = closer.register(openStream()); 297 return ByteStreams.readBytes(in, processor); 298 } catch (Throwable e) { 299 throw closer.rethrow(e); 300 } finally { 301 closer.close(); 302 } 303 } 304 305 /** 306 * Hashes the contents of this byte source using the given hash function. 307 * 308 * @throws IOException if an I/O error occurs in the process of reading from this source 309 */ 310 public HashCode hash(HashFunction hashFunction) throws IOException { 311 Hasher hasher = hashFunction.newHasher(); 312 copyTo(Funnels.asOutputStream(hasher)); 313 return hasher.hash(); 314 } 315 316 /** 317 * Checks that the contents of this byte source are equal to the contents of the given byte 318 * source. 319 * 320 * @throws IOException if an I/O error occurs in the process of reading from this source or 321 * {@code other} 322 */ 323 public boolean contentEquals(ByteSource other) throws IOException { 324 checkNotNull(other); 325 326 byte[] buf1 = new byte[BUF_SIZE]; 327 byte[] buf2 = new byte[BUF_SIZE]; 328 329 Closer closer = Closer.create(); 330 try { 331 InputStream in1 = closer.register(openStream()); 332 InputStream in2 = closer.register(other.openStream()); 333 while (true) { 334 int read1 = ByteStreams.read(in1, buf1, 0, BUF_SIZE); 335 int read2 = ByteStreams.read(in2, buf2, 0, BUF_SIZE); 336 if (read1 != read2 || !Arrays.equals(buf1, buf2)) { 337 return false; 338 } else if (read1 != BUF_SIZE) { 339 return true; 340 } 341 } 342 } catch (Throwable e) { 343 throw closer.rethrow(e); 344 } finally { 345 closer.close(); 346 } 347 } 348 349 /** 350 * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from 351 * the source will contain the concatenated data from the streams of the underlying sources. 352 * 353 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 354 * close the open underlying stream. 355 * 356 * @param sources the sources to concatenate 357 * @return a {@code ByteSource} containing the concatenated data 358 * @since 15.0 359 */ 360 public static ByteSource concat(Iterable<? extends ByteSource> sources) { 361 return new ConcatenatedByteSource(sources); 362 } 363 364 /** 365 * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from 366 * the source will contain the concatenated data from the streams of the underlying sources. 367 * 368 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 369 * close the open underlying stream. 370 * 371 * <p>Note: The input {@code Iterator} will be copied to an {@code ImmutableList} when this 372 * method is called. This will fail if the iterator is infinite and may cause problems if the 373 * iterator eagerly fetches data for each source when iterated (rather than producing sources 374 * that only load data through their streams). Prefer using the {@link #concat(Iterable)} 375 * overload if possible. 376 * 377 * @param sources the sources to concatenate 378 * @return a {@code ByteSource} containing the concatenated data 379 * @throws NullPointerException if any of {@code sources} is {@code null} 380 * @since 15.0 381 */ 382 public static ByteSource concat(Iterator<? extends ByteSource> sources) { 383 return concat(ImmutableList.copyOf(sources)); 384 } 385 386 /** 387 * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from 388 * the source will contain the concatenated data from the streams of the underlying sources. 389 * 390 * <p>Only one underlying stream will be open at a time. Closing the concatenated stream will 391 * close the open underlying stream. 392 * 393 * @param sources the sources to concatenate 394 * @return a {@code ByteSource} containing the concatenated data 395 * @throws NullPointerException if any of {@code sources} is {@code null} 396 * @since 15.0 397 */ 398 public static ByteSource concat(ByteSource... sources) { 399 return concat(ImmutableList.copyOf(sources)); 400 } 401 402 /** 403 * Returns a view of the given byte array as a {@link ByteSource}. To view only a specific range 404 * in the array, use {@code ByteSource.wrap(b).slice(offset, length)}. 405 * 406 * @since 15.0 (since 14.0 as {@code ByteStreams.asByteSource(byte[])}). 407 */ 408 public static ByteSource wrap(byte[] b) { 409 return new ByteArrayByteSource(b); 410 } 411 412 /** 413 * Returns an immutable {@link ByteSource} that contains no bytes. 414 * 415 * @since 15.0 416 */ 417 public static ByteSource empty() { 418 return EmptyByteSource.INSTANCE; 419 } 420 421 /** 422 * A char source that reads bytes from this source and decodes them as characters using a 423 * charset. 424 */ 425 private final class AsCharSource extends CharSource { 426 427 private final Charset charset; 428 429 private AsCharSource(Charset charset) { 430 this.charset = checkNotNull(charset); 431 } 432 433 @Override 434 public Reader openStream() throws IOException { 435 return new InputStreamReader(ByteSource.this.openStream(), charset); 436 } 437 438 @Override 439 public String toString() { 440 return ByteSource.this.toString() + ".asCharSource(" + charset + ")"; 441 } 442 } 443 444 /** 445 * A view of a subsection of the containing byte source. 446 */ 447 private final class SlicedByteSource extends ByteSource { 448 449 private final long offset; 450 private final long length; 451 452 private SlicedByteSource(long offset, long length) { 453 checkArgument(offset >= 0, "offset (%s) may not be negative", offset); 454 checkArgument(length >= 0, "length (%s) may not be negative", length); 455 this.offset = offset; 456 this.length = length; 457 } 458 459 @Override 460 public InputStream openStream() throws IOException { 461 return sliceStream(ByteSource.this.openStream()); 462 } 463 464 @Override 465 public InputStream openBufferedStream() throws IOException { 466 return sliceStream(ByteSource.this.openBufferedStream()); 467 } 468 469 private InputStream sliceStream(InputStream in) throws IOException { 470 if (offset > 0) { 471 try { 472 ByteStreams.skipFully(in, offset); 473 } catch (Throwable e) { 474 Closer closer = Closer.create(); 475 closer.register(in); 476 try { 477 throw closer.rethrow(e); 478 } finally { 479 closer.close(); 480 } 481 } 482 } 483 return ByteStreams.limit(in, length); 484 } 485 486 @Override 487 public ByteSource slice(long offset, long length) { 488 checkArgument(offset >= 0, "offset (%s) may not be negative", offset); 489 checkArgument(length >= 0, "length (%s) may not be negative", length); 490 long maxLength = this.length - offset; 491 return ByteSource.this.slice(this.offset + offset, Math.min(length, maxLength)); 492 } 493 494 @Override 495 public boolean isEmpty() throws IOException { 496 return length == 0 || super.isEmpty(); 497 } 498 499 @Override 500 public String toString() { 501 return ByteSource.this.toString() + ".slice(" + offset + ", " + length + ")"; 502 } 503 } 504 505 private static class ByteArrayByteSource extends ByteSource { 506 507 protected final byte[] bytes; 508 509 protected ByteArrayByteSource(byte[] bytes) { 510 this.bytes = checkNotNull(bytes); 511 } 512 513 @Override 514 public InputStream openStream() { 515 return new ByteArrayInputStream(bytes); 516 } 517 518 @Override 519 public InputStream openBufferedStream() throws IOException { 520 return openStream(); 521 } 522 523 @Override 524 public boolean isEmpty() { 525 return bytes.length == 0; 526 } 527 528 @Override 529 public long size() { 530 return bytes.length; 531 } 532 533 @Override 534 public byte[] read() { 535 return bytes.clone(); 536 } 537 538 @Override 539 public long copyTo(OutputStream output) throws IOException { 540 output.write(bytes); 541 return bytes.length; 542 } 543 544 @Override 545 public <T> T read(ByteProcessor<T> processor) throws IOException { 546 processor.processBytes(bytes, 0, bytes.length); 547 return processor.getResult(); 548 } 549 550 @Override 551 public HashCode hash(HashFunction hashFunction) throws IOException { 552 return hashFunction.hashBytes(bytes); 553 } 554 555 // TODO(user): Possibly override slice() 556 557 @Override 558 public String toString() { 559 return "ByteSource.wrap(" 560 + Ascii.truncate(BaseEncoding.base16().encode(bytes), 30, "...") + ")"; 561 } 562 } 563 564 private static final class EmptyByteSource extends ByteArrayByteSource { 565 566 private static final EmptyByteSource INSTANCE = new EmptyByteSource(); 567 568 private EmptyByteSource() { 569 super(new byte[0]); 570 } 571 572 @Override 573 public CharSource asCharSource(Charset charset) { 574 checkNotNull(charset); 575 return CharSource.empty(); 576 } 577 578 @Override 579 public byte[] read() { 580 return bytes; // length is 0, no need to clone 581 } 582 583 @Override 584 public String toString() { 585 return "ByteSource.empty()"; 586 } 587 } 588 589 private static final class ConcatenatedByteSource extends ByteSource { 590 591 private final Iterable<? extends ByteSource> sources; 592 593 ConcatenatedByteSource(Iterable<? extends ByteSource> sources) { 594 this.sources = checkNotNull(sources); 595 } 596 597 @Override 598 public InputStream openStream() throws IOException { 599 return new MultiInputStream(sources.iterator()); 600 } 601 602 @Override 603 public boolean isEmpty() throws IOException { 604 for (ByteSource source : sources) { 605 if (!source.isEmpty()) { 606 return false; 607 } 608 } 609 return true; 610 } 611 612 @Override 613 public long size() throws IOException { 614 long result = 0L; 615 for (ByteSource source : sources) { 616 result += source.size(); 617 } 618 return result; 619 } 620 621 @Override 622 public String toString() { 623 return "ByteSource.concat(" + sources + ")"; 624 } 625 } 626} 627