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