RandomAccessInputStream.java
/*
* public domain as of http://rsbweb.nih.gov/ij/disclaimer.html
*/
package com.github.junrar.io;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
/**
* This is a class that uses a memory cache to allow seeking within an
* InputStream. Based on the JAI MemoryCacheSeekableStream class.
*/
@SuppressWarnings("rawtypes")
public final class RandomAccessInputStream extends InputStream {
private static final int BLOCK_SIZE = 512;
private static final int BLOCK_MASK = 511;
private static final int BLOCK_SHIFT = 9;
private final InputStream src;
private long pointer;
private final Vector data;
private long length;
private boolean foundEOS;
/**
* Constructs a RandomAccessStream from an InputStream. Seeking backwards is
* supported using a memory cache.
*
* @param inputstream .
*/
public RandomAccessInputStream(InputStream inputstream) {
pointer = 0L;
data = new Vector();
length = 0;
foundEOS = false;
src = inputstream;
}
public int getFilePointer() throws IOException {
return (int) pointer;
}
public long getLongFilePointer() throws IOException {
return pointer;
}
public int read() throws IOException {
long l = pointer + 1L;
long l1 = readUntil(l);
if (l1 >= l) {
byte[] abyte0 = (byte[]) data
.elementAt((int) (pointer >>> BLOCK_SHIFT));
return abyte0[(int) (pointer++ & BLOCK_MASK)] & 0xff;
} else {
return -1;
}
}
public int read(byte[] bytes, int off, int len) throws IOException {
if (bytes == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || off + len > bytes.length) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}
long l = readUntil(pointer + len);
if (l <= pointer) {
return -1;
} else {
byte[] abyte1 = (byte[]) data
.elementAt((int) (pointer >>> BLOCK_SHIFT));
int k = Math.min(len, BLOCK_SIZE - (int) (pointer & BLOCK_MASK));
System.arraycopy(abyte1, (int) (pointer & BLOCK_MASK), bytes, off,
k);
pointer += k;
return k;
}
}
public void readFully(byte[] bytes) throws IOException {
readFully(bytes, bytes.length);
}
public void readFully(byte[] bytes, int len) throws IOException {
int read = 0;
do {
int l = read(bytes, read, len - read);
if (l < 0) {
break;
}
read += l;
} while (read < len);
}
@SuppressWarnings("unchecked")
private long readUntil(long l) throws IOException {
if (l < length) {
return l;
}
if (foundEOS) {
return length;
}
int i = (int) (l >>> BLOCK_SHIFT);
int j = (int) (length >>> BLOCK_SHIFT);
for (int k = j; k <= i; k++) {
byte[] abyte0 = new byte[BLOCK_SIZE];
data.addElement(abyte0);
int i1 = BLOCK_SIZE;
int j1 = 0;
while (i1 > 0) {
int k1 = src.read(abyte0, j1, i1);
if (k1 == -1) {
foundEOS = true;
return length;
}
j1 += k1;
i1 -= k1;
length += k1;
}
}
return length;
}
public void seek(long loc) throws IOException {
if (loc < 0L) {
pointer = 0L;
} else {
pointer = loc;
}
}
public void seek(int loc) throws IOException {
long lloc = ((long) loc) & 0xffffffffL;
if (lloc < 0L) {
pointer = 0L;
} else {
pointer = lloc;
}
}
public int readInt() throws IOException {
int i = read();
int j = read();
int k = read();
int l = read();
if ((i | j | k | l) < 0) {
throw new EOFException();
} else {
return (i << 24) + (j << 16) + (k << 8) + l;
}
}
public long readLong() throws IOException {
return ((long) readInt() << 32) + ((long) readInt() & 0xffffffffL);
}
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
public short readShort() throws IOException {
int i = read();
int j = read();
if ((i | j) < 0) {
throw new EOFException();
} else {
return (short) ((i << 8) + j);
}
}
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
public void close() throws IOException {
data.removeAllElements();
src.close();
}
}