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        /**
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    }