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.apache.kahadb.util;
018    
019    import java.io.DataInput;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.UTFDataFormatException;
023    
024    /**
025     * Optimized ByteArrayInputStream that can be used more than once
026     *
027     *
028     */
029    public 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    }