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