View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.buffer;
17  
18  import java.io.DataInput;
19  import java.io.DataInputStream;
20  import java.io.EOFException;
21  import java.io.IOException;
22  import java.io.InputStream;
23  
24  /**
25   * An {@link InputStream} which reads data from a {@link ByteBuf}.
26   * <p>
27   * A read operation against this stream will occur at the {@code readerIndex}
28   * of its underlying buffer and the {@code readerIndex} will increase during
29   * the read operation.  Please note that it only reads up to the number of
30   * readable bytes determined at the moment of construction.  Therefore,
31   * updating {@link ByteBuf#writerIndex()} will not affect the return
32   * value of {@link #available()}.
33   * <p>
34   * This stream implements {@link DataInput} for your convenience.
35   * The endianness of the stream is not always big endian but depends on
36   * the endianness of the underlying buffer.
37   *
38   * @see ByteBufOutputStream
39   */
40  public class ByteBufInputStream extends InputStream implements DataInput {
41  
42      private final ByteBuf buffer;
43      private final int startIndex;
44      private final int endIndex;
45  
46      /**
47       * Creates a new stream which reads data from the specified {@code buffer}
48       * starting at the current {@code readerIndex} and ending at the current
49       * {@code writerIndex}.
50       */
51      public ByteBufInputStream(ByteBuf buffer) {
52          this(buffer, buffer.readableBytes());
53      }
54  
55      /**
56       * Creates a new stream which reads data from the specified {@code buffer}
57       * starting at the current {@code readerIndex} and ending at
58       * {@code readerIndex + length}.
59       *
60       * @throws IndexOutOfBoundsException
61       *         if {@code readerIndex + length} is greater than
62       *            {@code writerIndex}
63       */
64      public ByteBufInputStream(ByteBuf buffer, int length) {
65          if (buffer == null) {
66              throw new NullPointerException("buffer");
67          }
68          if (length < 0) {
69              throw new IllegalArgumentException("length: " + length);
70          }
71          if (length > buffer.readableBytes()) {
72              throw new IndexOutOfBoundsException("Too many bytes to be read - Needs "
73                      + length + ", maximum is " + buffer.readableBytes());
74          }
75  
76          this.buffer = buffer;
77          startIndex = buffer.readerIndex();
78          endIndex = startIndex + length;
79          buffer.markReaderIndex();
80      }
81  
82      /**
83       * Returns the number of read bytes by this stream so far.
84       */
85      public int readBytes() {
86          return buffer.readerIndex() - startIndex;
87      }
88  
89      @Override
90      public int available() throws IOException {
91          return endIndex - buffer.readerIndex();
92      }
93  
94      @Override
95      public void mark(int readlimit) {
96          buffer.markReaderIndex();
97      }
98  
99      @Override
100     public boolean markSupported() {
101         return true;
102     }
103 
104     @Override
105     public int read() throws IOException {
106         if (!buffer.isReadable()) {
107             return -1;
108         }
109         return buffer.readByte() & 0xff;
110     }
111 
112     @Override
113     public int read(byte[] b, int off, int len) throws IOException {
114         int available = available();
115         if (available == 0) {
116             return -1;
117         }
118 
119         len = Math.min(available, len);
120         buffer.readBytes(b, off, len);
121         return len;
122     }
123 
124     @Override
125     public void reset() throws IOException {
126         buffer.resetReaderIndex();
127     }
128 
129     @Override
130     public long skip(long n) throws IOException {
131         if (n > Integer.MAX_VALUE) {
132             return skipBytes(Integer.MAX_VALUE);
133         } else {
134             return skipBytes((int) n);
135         }
136     }
137 
138     @Override
139     public boolean readBoolean() throws IOException {
140         checkAvailable(1);
141         return read() != 0;
142     }
143 
144     @Override
145     public byte readByte() throws IOException {
146         if (!buffer.isReadable()) {
147             throw new EOFException();
148         }
149         return buffer.readByte();
150     }
151 
152     @Override
153     public char readChar() throws IOException {
154         return (char) readShort();
155     }
156 
157     @Override
158     public double readDouble() throws IOException {
159         return Double.longBitsToDouble(readLong());
160     }
161 
162     @Override
163     public float readFloat() throws IOException {
164         return Float.intBitsToFloat(readInt());
165     }
166 
167     @Override
168     public void readFully(byte[] b) throws IOException {
169         readFully(b, 0, b.length);
170     }
171 
172     @Override
173     public void readFully(byte[] b, int off, int len) throws IOException {
174         checkAvailable(len);
175         buffer.readBytes(b, off, len);
176     }
177 
178     @Override
179     public int readInt() throws IOException {
180         checkAvailable(4);
181         return buffer.readInt();
182     }
183 
184     private final StringBuilder lineBuf = new StringBuilder();
185 
186     @Override
187     public String readLine() throws IOException {
188         lineBuf.setLength(0);
189 
190         loop: while (true) {
191             if (!buffer.isReadable()) {
192                 return lineBuf.length() > 0 ? lineBuf.toString() : null;
193             }
194 
195             int c = buffer.readUnsignedByte();
196             switch (c) {
197                 case '\n':
198                     break loop;
199 
200                 case '\r':
201                     if (buffer.isReadable() && (char) buffer.getUnsignedByte(buffer.readerIndex()) == '\n') {
202                         buffer.skipBytes(1);
203                     }
204                     break loop;
205 
206                 default:
207                     lineBuf.append((char) c);
208             }
209         }
210 
211         return lineBuf.toString();
212     }
213 
214     @Override
215     public long readLong() throws IOException {
216         checkAvailable(8);
217         return buffer.readLong();
218     }
219 
220     @Override
221     public short readShort() throws IOException {
222         checkAvailable(2);
223         return buffer.readShort();
224     }
225 
226     @Override
227     public String readUTF() throws IOException {
228         return DataInputStream.readUTF(this);
229     }
230 
231     @Override
232     public int readUnsignedByte() throws IOException {
233         return readByte() & 0xff;
234     }
235 
236     @Override
237     public int readUnsignedShort() throws IOException {
238         return readShort() & 0xffff;
239     }
240 
241     @Override
242     public int skipBytes(int n) throws IOException {
243         int nBytes = Math.min(available(), n);
244         buffer.skipBytes(nBytes);
245         return nBytes;
246     }
247 
248     private void checkAvailable(int fieldSize) throws IOException {
249         if (fieldSize < 0) {
250             throw new IndexOutOfBoundsException("fieldSize cannot be a negative number");
251         }
252         if (fieldSize > available()) {
253             throw new EOFException("fieldSize is too long! Length is " + fieldSize
254                     + ", but maximum is " + available());
255         }
256     }
257 }