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