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 org.jboss.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 ChannelBuffer}.
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.
30   * <p>
31   * This stream implements {@link DataInput} for your convenience.
32   * The endianness of the stream is not always big endian but depends on
33   * the endianness of the underlying buffer.
34   *
35   * @see ChannelBufferOutputStream
36   * @apiviz.uses org.jboss.netty.buffer.ChannelBuffer
37   */
38  public class ChannelBufferInputStream extends InputStream implements DataInput {
39  
40      private final ChannelBuffer buffer;
41      private final int startIndex;
42      private final int endIndex;
43  
44      /**
45       * Creates a new stream which reads data from the specified {@code buffer}
46       * starting at the current {@code readerIndex} and ending at the current
47       * {@code writerIndex}.
48       */
49      public ChannelBufferInputStream(ChannelBuffer buffer) {
50          this(buffer, buffer.readableBytes());
51      }
52  
53      /**
54       * Creates a new stream which reads data from the specified {@code buffer}
55       * starting at the current {@code readerIndex} and ending at
56       * {@code readerIndex + length}.
57       *
58       * @throws IndexOutOfBoundsException
59       *         if {@code readerIndex + length} is greater than
60       *            {@code writerIndex}
61       */
62      public ChannelBufferInputStream(ChannelBuffer buffer, int length) {
63          if (buffer == null) {
64              throw new NullPointerException("buffer");
65          }
66          if (length < 0) {
67              throw new IllegalArgumentException("length: " + length);
68          }
69          if (length > buffer.readableBytes()) {
70              throw new IndexOutOfBoundsException("Too many bytes to be read - Needs "
71                      + length + ", maximum is " + buffer.readableBytes());
72          }
73  
74          this.buffer = buffer;
75          startIndex = buffer.readerIndex();
76          endIndex = startIndex + length;
77          buffer.markReaderIndex();
78      }
79  
80      /**
81       * Returns the number of read bytes by this stream so far.
82       */
83      public int readBytes() {
84          return buffer.readerIndex() - startIndex;
85      }
86  
87      @Override
88      public int available() throws IOException {
89          return endIndex - buffer.readerIndex();
90      }
91  
92      @Override
93      public void mark(int readlimit) {
94          buffer.markReaderIndex();
95      }
96  
97      @Override
98      public boolean markSupported() {
99          return true;
100     }
101 
102     @Override
103     public int read() throws IOException {
104         if (!buffer.readable()) {
105             return -1;
106         }
107         return buffer.readByte() & 0xff;
108     }
109 
110     @Override
111     public int read(byte[] b, int off, int len) throws IOException {
112         int available = available();
113         if (available == 0) {
114             return -1;
115         }
116 
117         len = Math.min(available, len);
118         buffer.readBytes(b, off, len);
119         return len;
120     }
121 
122     @Override
123     public void reset() throws IOException {
124         buffer.resetReaderIndex();
125     }
126 
127     @Override
128     public long skip(long n) throws IOException {
129         if (n > Integer.MAX_VALUE) {
130             return skipBytes(Integer.MAX_VALUE);
131         } else {
132             return skipBytes((int) n);
133         }
134     }
135 
136     public boolean readBoolean() throws IOException {
137         checkAvailable(1);
138         return read() != 0;
139     }
140 
141     public byte readByte() throws IOException {
142         if (!buffer.readable()) {
143             throw new EOFException();
144         }
145         return buffer.readByte();
146     }
147 
148     public char readChar() throws IOException {
149         return (char) readShort();
150     }
151 
152     public double readDouble() throws IOException {
153         return Double.longBitsToDouble(readLong());
154     }
155 
156     public float readFloat() throws IOException {
157         return Float.intBitsToFloat(readInt());
158     }
159 
160     public void readFully(byte[] b) throws IOException {
161         readFully(b, 0, b.length);
162     }
163 
164     public void readFully(byte[] b, int off, int len) throws IOException {
165         checkAvailable(len);
166         buffer.readBytes(b, off, len);
167     }
168 
169     public int readInt() throws IOException {
170         checkAvailable(4);
171         return buffer.readInt();
172     }
173 
174     private final StringBuilder lineBuf = new StringBuilder();
175 
176     public String readLine() throws IOException {
177         lineBuf.setLength(0);
178         for (;;) {
179             int b = read();
180             if (b < 0 || b == '\n') {
181                 break;
182             }
183 
184             lineBuf.append((char) b);
185         }
186 
187         if (lineBuf.length() > 0) {
188             while (lineBuf.charAt(lineBuf.length() - 1) == '\r') {
189                 lineBuf.setLength(lineBuf.length() - 1);
190             }
191         }
192 
193         return lineBuf.toString();
194     }
195 
196     public long readLong() throws IOException {
197         checkAvailable(8);
198         return buffer.readLong();
199     }
200 
201     public short readShort() throws IOException {
202         checkAvailable(2);
203         return buffer.readShort();
204     }
205 
206     public String readUTF() throws IOException {
207         return DataInputStream.readUTF(this);
208     }
209 
210     public int readUnsignedByte() throws IOException {
211         return readByte() & 0xff;
212     }
213 
214     public int readUnsignedShort() throws IOException {
215         return readShort() & 0xffff;
216     }
217 
218     public int skipBytes(int n) throws IOException {
219         int nBytes = Math.min(available(), n);
220         buffer.skipBytes(nBytes);
221         return nBytes;
222     }
223 
224     private void checkAvailable(int fieldSize) throws IOException {
225         if (fieldSize < 0) {
226             throw new IndexOutOfBoundsException("fieldSize cannot be a negative number");
227         }
228         if (fieldSize > available()) {
229             throw new EOFException("fieldSize is too long! Length is " + fieldSize
230                     + ", but maximum is " + available());
231         }
232     }
233 }