001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.fusesource.hawtbuf;
018    
019    import java.io.DataInput;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.UTFDataFormatException;
023    
024    
025    /**
026     * Optimized ByteArrayInputStream that can be used more than once
027     * 
028     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
029     */
030    public final class DataByteArrayInputStream extends InputStream implements DataInput {
031        private byte[] buf;
032        private int pos;
033        private int offset;
034        private int length;
035    
036        /**
037         * Creates a <code>StoreByteArrayInputStream</code>.
038         * 
039         * @param buf the input buffer.
040         */
041        public DataByteArrayInputStream(byte buf[]) {
042            this.buf = buf;
043            this.pos = 0;
044            this.offset = 0;
045            this.length = buf.length;
046        }
047    
048        /**
049         * Creates a <code>StoreByteArrayInputStream</code>.
050         * 
051         * @param sequence the input buffer.
052         */
053        public DataByteArrayInputStream(Buffer sequence) {
054            this.buf = sequence.getData();
055            this.offset = sequence.getOffset();
056            this.pos =  this.offset;
057            this.length = sequence.length;
058        }
059    
060        /**
061         * Creates <code>WireByteArrayInputStream</code> with a minmalist byte
062         * array
063         */
064        public DataByteArrayInputStream() {
065            this(new byte[0]);
066        }
067    
068        /**
069         * @return the size
070         */
071        public int size() {
072            return pos - offset;
073        }
074    
075        /**
076         * @return the underlying data array
077         */
078        public byte[] getRawData() {
079            return buf;
080        }
081    
082        /**
083         * reset the <code>StoreByteArrayInputStream</code> to use an new byte
084         * array
085         * 
086         * @param newBuff
087         */
088        public void restart(byte[] newBuff) {
089            buf = newBuff;
090            pos = 0;
091            length = newBuff.length;
092        }
093    
094        public void restart() {
095            pos = 0;
096            length = buf.length;
097        }
098    
099        /**
100         * reset the <code>StoreByteArrayInputStream</code> to use an new
101         * Buffer
102         * 
103         * @param sequence
104         */
105        public void restart(Buffer sequence) {
106            this.buf = sequence.getData();
107            this.pos = sequence.getOffset();
108            this.length = sequence.getLength();
109        }
110    
111        /**
112         * re-start the input stream - reusing the current buffer
113         * 
114         * @param size
115         */
116        public void restart(int size) {
117            if (buf == null || buf.length < size) {
118                buf = new byte[size];
119            }
120            restart(buf);
121            this.length = size;
122        }
123    
124        /**
125         * Reads the next byte of data from this input stream. The value byte is
126         * returned as an <code>int</code> in the range <code>0</code> to
127         * <code>255</code>. If no byte is available because the end of the
128         * stream has been reached, the value <code>-1</code> is returned.
129         * <p>
130         * This <code>read</code> method cannot block.
131         * 
132         * @return the next byte of data, or <code>-1</code> if the end of the
133         *         stream has been reached.
134         */
135        public int read() {
136            return (pos < length) ? (buf[pos++] & 0xff) : -1;
137        }
138    
139        /**
140         * Reads up to <code>len</code> bytes of data into an array of bytes from
141         * this input stream.
142         * 
143         * @param b the buffer into which the data is read.
144         * @param off the start offset of the data.
145         * @param len the maximum number of bytes read.
146         * @return the total number of bytes read into the buffer, or
147         *         <code>-1</code> if there is no more data because the end of the
148         *         stream has been reached.
149         */
150        public int read(byte b[], int off, int len) {
151            if (b == null) {
152                throw new NullPointerException();
153            }
154            if (pos >= length) {
155                return -1;
156            }
157            if (pos + len > length) {
158                len = length - pos;
159            }
160            if (len <= 0) {
161                return 0;
162            }
163            System.arraycopy(buf, pos, b, off, len);
164            pos += len;
165            return len;
166        }
167    
168        /**
169         * @return the number of bytes that can be read from the input stream
170         *         without blocking.
171         */
172        public int available() {
173            return length - pos;
174        }
175    
176        public void readFully(byte[] b) {
177            read(b, 0, b.length);
178        }
179    
180        public void readFully(byte[] b, int off, int len) {
181            read(b, off, len);
182        }
183    
184        public int skipBytes(int n) {
185            if (pos + n > length) {
186                n = length - pos;
187            }
188            if (n < 0) {
189                return 0;
190            }
191            pos += n;
192            return n;
193        }
194    
195        public boolean readBoolean() {
196            return read() != 0;
197        }
198    
199        public byte readByte() {
200            return (byte)read();
201        }
202    
203        public int readUnsignedByte() {
204            return read();
205        }
206    
207        public short readShort() {
208            int ch1 = read();
209            int ch2 = read();
210            return (short)((ch1 << 8) + (ch2 << 0));
211        }
212    
213        public int readUnsignedShort() {
214            int ch1 = read();
215            int ch2 = read();
216            return (ch1 << 8) + (ch2 << 0);
217        }
218    
219        public char readChar() {
220            int ch1 = read();
221            int ch2 = read();
222            return (char)((ch1 << 8) + (ch2 << 0));
223        }
224    
225        public int readInt() {
226            int ch1 = read();
227            int ch2 = read();
228            int ch3 = read();
229            int ch4 = read();
230            return (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0);
231        }
232    
233        public long readLong() {
234            long rc = ((long)buf[pos++] << 56) + ((long)(buf[pos++] & 255) << 48) + ((long)(buf[pos++] & 255) << 40) + ((long)(buf[pos++] & 255) << 32);
235            return rc + ((long)(buf[pos++] & 255) << 24) + ((buf[pos++] & 255) << 16) + ((buf[pos++] & 255) << 8) + ((buf[pos++] & 255) << 0);
236        }
237    
238        public float readFloat() throws IOException {
239            return Float.intBitsToFloat(readInt());
240        }
241    
242        public double readDouble() throws IOException {
243            return Double.longBitsToDouble(readLong());
244        }
245    
246        public String readLine() {
247            int start = pos;
248            while (pos < length) {
249                int c = read();
250                if (c == '\n') {
251                    break;
252                }
253                if (c == '\r') {
254                    c = read();
255                    if (c != '\n' && c != -1) {
256                        pos--;
257                    }
258                    break;
259                }
260            }
261            return new String(buf, start, pos);
262        }
263    
264        public String readUTF() throws IOException {
265            int length = readUnsignedShort();
266            char[] characters = new char[length];
267            int c;
268            int c2;
269            int c3;
270            int count = 0;
271            int total = pos + length;
272            while (pos < total) {
273                c = (int)buf[pos] & 0xff;
274                if (c > 127) {
275                    break;
276                }
277                pos++;
278                characters[count++] = (char)c;
279            }
280            while (pos < total) {
281                c = (int)buf[pos] & 0xff;
282                switch (c >> 4) {
283                case 0:
284                case 1:
285                case 2:
286                case 3:
287                case 4:
288                case 5:
289                case 6:
290                case 7:
291                    pos++;
292                    characters[count++] = (char)c;
293                    break;
294                case 12:
295                case 13:
296                    pos += 2;
297                    if (pos > total) {
298                        throw new UTFDataFormatException("bad string");
299                    }
300                    c2 = (int)buf[pos - 1];
301                    if ((c2 & 0xC0) != 0x80) {
302                        throw new UTFDataFormatException("bad string");
303                    }
304                    characters[count++] = (char)(((c & 0x1F) << 6) | (c2 & 0x3F));
305                    break;
306                case 14:
307                    pos += 3;
308                    if (pos > total) {
309                        throw new UTFDataFormatException("bad string");
310                    }
311                    c2 = (int)buf[pos - 2];
312                    c3 = (int)buf[pos - 1];
313                    if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
314                        throw new UTFDataFormatException("bad string");
315                    }
316                    characters[count++] = (char)(((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | ((c3 & 0x3F) << 0));
317                    break;
318                default:
319                    throw new UTFDataFormatException("bad string");
320                }
321            }
322            return new String(characters, 0, count);
323        }
324    
325        public int getPos() {
326            return pos;
327        }
328    
329        public void setPos(int pos) {
330            this.pos = pos;
331        }
332    
333        public int getLength() {
334            return length;
335        }
336    
337        public void setLength(int length) {
338            this.length = length;
339        }
340    }