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 }