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