1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.hdfs.util;
19 
20 import java.io.EOFException;
21 import java.io.FilterInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 
25 import org.apache.hadoop.classification.InterfaceAudience;
26 import org.apache.hadoop.classification.InterfaceStability;
27 
28 import com.google.common.base.Preconditions;
29 
30 /**
31  * An InputStream implementations which reads from some other InputStream
32  * but expects an exact number of bytes. Any attempts to read past the
33  * specified number of bytes will return as if the end of the stream
34  * was reached. If the end of the underlying stream is reached prior to
35  * the specified number of bytes, an EOFException is thrown.
36  */
37 @InterfaceAudience.Private
38 @InterfaceStability.Evolving
39 public class ExactSizeInputStream extends FilterInputStream {
40   private int remaining;
41 
42   /**
43    * Construct an input stream that will read no more than
44    * 'numBytes' bytes.
45    *
46    * If an EOF occurs on the underlying stream before numBytes
47    * bytes have been read, an EOFException will be thrown.
48    *
49    * @param in the inputstream to wrap
50    * @param numBytes the number of bytes to read
51    */
ExactSizeInputStream(InputStream in, int numBytes)52   public ExactSizeInputStream(InputStream in, int numBytes) {
53     super(in);
54     Preconditions.checkArgument(numBytes >= 0,
55         "Negative expected bytes: ", numBytes);
56     this.remaining = numBytes;
57   }
58 
59   @Override
available()60   public int available() throws IOException {
61     return Math.min(super.available(), remaining);
62   }
63 
64   @Override
read()65   public int read() throws IOException {
66     // EOF if we reached our limit
67     if (remaining <= 0) {
68       return -1;
69     }
70     final int result = super.read();
71     if (result >= 0) {
72       --remaining;
73     } else if (remaining > 0) {
74       // Underlying stream reached EOF but we haven't read the expected
75       // number of bytes.
76       throw new EOFException(
77           "Premature EOF. Expected " + remaining + "more bytes");
78     }
79     return result;
80   }
81 
82   @Override
read(final byte[] b, final int off, int len)83   public int read(final byte[] b, final int off, int len)
84                   throws IOException {
85     if (remaining <= 0) {
86       return -1;
87     }
88     len = Math.min(len, remaining);
89     final int result = super.read(b, off, len);
90     if (result >= 0) {
91       remaining -= result;
92     } else if (remaining > 0) {
93       // Underlying stream reached EOF but we haven't read the expected
94       // number of bytes.
95       throw new EOFException(
96           "Premature EOF. Expected " + remaining + "more bytes");
97     }
98     return result;
99   }
100 
101   @Override
skip(final long n)102   public long skip(final long n) throws IOException {
103     final long result = super.skip(Math.min(n, remaining));
104     if (result > 0) {
105       remaining -= result;
106     } else if (remaining > 0) {
107       // Underlying stream reached EOF but we haven't read the expected
108       // number of bytes.
109       throw new EOFException(
110           "Premature EOF. Expected " + remaining + "more bytes");
111     }
112     return result;
113   }
114 
115   @Override
markSupported()116   public boolean markSupported() {
117     return false;
118   }
119 
120   @Override
mark(int readlimit)121   public void mark(int readlimit) {
122     throw new UnsupportedOperationException();
123   }
124 
125 }