LimitedInputStream.java

  1. /*
  2.  * Copyright (C) 2007 The Guava Authors
  3.  * Copyright (C) 2014, Sasa Zivkov <sasa.zivkov@sap.com>, SAP AG and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

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

  12. import java.io.FilterInputStream;
  13. import java.io.IOException;
  14. import java.io.InputStream;

  15. import org.eclipse.jgit.internal.JGitText;

  16. /**
  17.  * Wraps a {@link java.io.InputStream}, limiting the number of bytes which can
  18.  * be read.
  19.  *
  20.  * This class was copied and modifed from the Google Guava 16.0. Differently
  21.  * from the original Guava code, when a caller tries to read from this stream
  22.  * past the given limit and the wrapped stream hasn't yet reached its EOF this
  23.  * class will call the limitExceeded method instead of returning EOF.
  24.  *
  25.  * @since 3.3
  26.  */
  27. public abstract class LimitedInputStream extends FilterInputStream {

  28.     private long left;
  29.     /** Max number of bytes to be read from the wrapped stream */
  30.     protected final long limit;
  31.     private long mark = -1;

  32.     /**
  33.      * Create a new LimitedInputStream
  34.      *
  35.      * @param in an InputStream
  36.      * @param limit max number of bytes to read from the InputStream
  37.      */
  38.     protected LimitedInputStream(InputStream in, long limit) {
  39.         super(in);
  40.         left = limit;
  41.         this.limit = limit;
  42.     }

  43.     /** {@inheritDoc} */
  44.     @Override
  45.     public int available() throws IOException {
  46.         return (int) Math.min(in.available(), left);
  47.     }

  48.     // it's okay to mark even if mark isn't supported, as reset won't work
  49.     /** {@inheritDoc} */
  50.     @Override
  51.     public synchronized void mark(int readLimit) {
  52.         in.mark(readLimit);
  53.         mark = left;
  54.     }

  55.     /** {@inheritDoc} */
  56.     @Override
  57.     public int read() throws IOException {
  58.         if (left == 0) {
  59.             if (in.available() == 0) {
  60.                 return -1;
  61.             }
  62.             limitExceeded();
  63.         }

  64.         int result = in.read();
  65.         if (result != -1) {
  66.             --left;
  67.         }
  68.         return result;
  69.     }

  70.     /** {@inheritDoc} */
  71.     @Override
  72.     public int read(byte[] b, int off, int len) throws IOException {
  73.         if (left == 0) {
  74.             if (in.available() == 0) {
  75.                 return -1;
  76.             }
  77.             limitExceeded();
  78.         }

  79.         len = (int) Math.min(len, left);
  80.         int result = in.read(b, off, len);
  81.         if (result != -1) {
  82.             left -= result;
  83.         }
  84.         return result;
  85.     }

  86.     /** {@inheritDoc} */
  87.     @Override
  88.     public synchronized void reset() throws IOException {
  89.         if (!in.markSupported())
  90.             throw new IOException(JGitText.get().unsupportedMark);

  91.         if (mark == -1)
  92.             throw new IOException(JGitText.get().unsetMark);

  93.         in.reset();
  94.         left = mark;
  95.     }

  96.     /** {@inheritDoc} */
  97.     @Override
  98.     public long skip(long n) throws IOException {
  99.         n = Math.min(n, left);
  100.         long skipped = in.skip(n);
  101.         left -= skipped;
  102.         return skipped;
  103.     }

  104.     /**
  105.      * Called when trying to read past the given {@link #limit} and the wrapped
  106.      * InputStream {@link #in} hasn't yet reached its EOF
  107.      *
  108.      * @throws java.io.IOException
  109.      *             subclasses can throw an {@link java.io.IOException} when the
  110.      *             limit is exceeded. The throws java.io.IOException will be
  111.      *             forwarded back to the caller of the read method which read
  112.      *             the stream past the limit.
  113.      */
  114.     protected abstract void limitExceeded() throws IOException;
  115. }