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