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