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