TeeInputStream.java

  1. /*
  2.  * Copyright (C) 2010, 2013 Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.util.io;

  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.io.OutputStream;

  14. /**
  15.  * Input stream that copies data read to another output stream.
  16.  *
  17.  * This stream is primarily useful with a
  18.  * {@link org.eclipse.jgit.util.TemporaryBuffer}, where any data read or skipped
  19.  * by the caller is also duplicated into the temporary buffer. Later the
  20.  * temporary buffer can then be used instead of the original source stream.
  21.  *
  22.  * During close this stream copies any remaining data from the source stream
  23.  * into the destination stream.
  24.  */
  25. public class TeeInputStream extends InputStream {
  26.     private byte[] skipBuffer;

  27.     private InputStream src;

  28.     private OutputStream dst;

  29.     /**
  30.      * Initialize a tee input stream.
  31.      *
  32.      * @param src
  33.      *            source stream to consume.
  34.      * @param dst
  35.      *            destination to copy the source to as it is consumed. Typically
  36.      *            this is a {@link org.eclipse.jgit.util.TemporaryBuffer}.
  37.      */
  38.     public TeeInputStream(InputStream src, OutputStream dst) {
  39.         this.src = src;
  40.         this.dst = dst;
  41.     }

  42.     /** {@inheritDoc} */
  43.     @Override
  44.     public int read() throws IOException {
  45.         byte[] b = skipBuffer();
  46.         int n = read(b, 0, 1);
  47.         return n == 1 ? b[0] & 0xff : -1;
  48.     }

  49.     /** {@inheritDoc} */
  50.     @Override
  51.     public long skip(long count) throws IOException {
  52.         long skipped = 0;
  53.         long cnt = count;
  54.         final byte[] b = skipBuffer();
  55.         while (0 < cnt) {
  56.             final int n = src.read(b, 0, (int) Math.min(b.length, cnt));
  57.             if (n <= 0)
  58.                 break;
  59.             dst.write(b, 0, n);
  60.             skipped += n;
  61.             cnt -= n;
  62.         }
  63.         return skipped;
  64.     }

  65.     /** {@inheritDoc} */
  66.     @Override
  67.     public int read(byte[] b, int off, int len) throws IOException {
  68.         if (len == 0)
  69.             return 0;

  70.         int n = src.read(b, off, len);
  71.         if (0 < n)
  72.             dst.write(b, off, n);
  73.         return n;
  74.     }

  75.     /** {@inheritDoc} */
  76.     @Override
  77.     public void close() throws IOException {
  78.         byte[] b = skipBuffer();
  79.         for (;;) {
  80.             int n = src.read(b);
  81.             if (n <= 0)
  82.                 break;
  83.             dst.write(b, 0, n);
  84.         }
  85.         dst.close();
  86.         src.close();
  87.     }

  88.     private byte[] skipBuffer() {
  89.         if (skipBuffer == null)
  90.             skipBuffer = new byte[2048];
  91.         return skipBuffer;
  92.     }
  93. }