001    /*
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one
004     * or more contributor license agreements.  See the NOTICE file
005     * distributed with this work for additional information
006     * regarding copyright ownership.  The ASF licenses this file
007     * to you under the Apache License, Version 2.0 (the
008     * "License"); you may not use this file except in compliance
009     * with the License.  You may obtain a copy of the License at
010     *
011     *     http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    package org.apache.hadoop.util;
021    
022    import static com.google.common.base.Preconditions.checkArgument;
023    import static com.google.common.base.Preconditions.checkNotNull;
024    
025    import java.io.FilterInputStream;
026    import java.io.IOException;
027    import java.io.InputStream;
028    
029    import org.apache.hadoop.classification.InterfaceStability.Unstable;
030    
031    /**
032     * Copied from guava source code v15 (LimitedInputStream)
033     * Guava deprecated LimitInputStream in v14 and removed it in v15. Copying this class here
034     * allows to be compatible with guava 11 to 15+.
035     * 
036     * Originally: org.apache.hadoop.hbase.io.LimitInputStream
037     */
038    @Unstable
039    public final class LimitInputStream extends FilterInputStream {
040      private long left;
041      private long mark = -1;
042    
043      public LimitInputStream(InputStream in, long limit) {
044        super(in);
045        checkNotNull(in);
046        checkArgument(limit >= 0, "limit must be non-negative");
047        left = limit;
048      }
049    
050      @Override
051      public int available() throws IOException {
052        return (int) Math.min(in.available(), left);
053      }
054    
055      // it's okay to mark even if mark isn't supported, as reset won't work
056      @Override
057      public synchronized void mark(int readLimit) {
058        in.mark(readLimit);
059        mark = left;
060      }
061    
062      @Override
063      public int read() throws IOException {
064        if (left == 0) {
065          return -1;
066        }
067    
068        int result = in.read();
069        if (result != -1) {
070          --left;
071        }
072        return result;
073      }
074    
075      @Override
076      public int read(byte[] b, int off, int len) throws IOException {
077        if (left == 0) {
078          return -1;
079        }
080    
081        len = (int) Math.min(len, left);
082        int result = in.read(b, off, len);
083        if (result != -1) {
084          left -= result;
085        }
086        return result;
087      }
088    
089      @Override
090      public synchronized void reset() throws IOException {
091        if (!in.markSupported()) {
092          throw new IOException("Mark not supported");
093        }
094        if (mark == -1) {
095          throw new IOException("Mark not set");
096        }
097    
098        in.reset();
099        left = mark;
100      }
101    
102      @Override
103      public long skip(long n) throws IOException {
104        n = Math.min(n, left);
105        long skipped = in.skip(n);
106        left -= skipped;
107        return skipped;
108      }
109    }