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