package ij.io; import ij.IJ; import java.io.*; 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. Can also be constructed from a RandomAccessFile, which uses less memory since the memory cache is not required. */ public final class RandomAccessStream extends InputStream { private static final int BLOCK_SIZE = 512; private static final int BLOCK_MASK = 511; private static final int BLOCK_SHIFT = 9; private InputStream src; private RandomAccessFile ras; private long pointer; private Vector data; private int length; private boolean foundEOS; /** Constructs a RandomAccessStream from an InputStream. Seeking backwards is supported using a memory cache. */ public RandomAccessStream(InputStream inputstream) { pointer = 0L; data = new Vector(); length = 0; foundEOS = false; src = inputstream; } /** Constructs a RandomAccessStream from an RandomAccessFile. */ public RandomAccessStream(RandomAccessFile ras) { this.ras = ras; } public int getFilePointer() throws IOException { if (ras!=null) return (int)ras.getFilePointer(); else return (int)pointer; } public long getLongFilePointer() throws IOException { if (ras!=null) return ras.getFilePointer(); else return pointer; } public int read() throws IOException { if (ras!=null) return ras.read(); 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 (ras!=null) return ras.read(bytes, off, len); 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 final void readFully(byte[] bytes) throws IOException { readFully(bytes, bytes.length); } public final 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); } private long readUntil(long l) throws IOException { if(l<length) return l; if(foundEOS) return length; int i = (int)(l >> BLOCK_SHIFT); int j = 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 { //IJ.log("seek (long): "+loc+" "+(ras!=null)); if (ras!=null) {ras.seek(loc); return;} if (loc<0L) pointer = 0L; else pointer = loc; } public void seek(int loc) throws IOException { long lloc = ((long)loc)&0xffffffffL; //IJ.log("seek (int): "+lloc+" "+(ras!=null)); if (ras!=null) { ras.seek(lloc); return; } if (lloc<0L) pointer = 0L; else pointer = lloc; } public final 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 final long readLong() throws IOException { return ((long)readInt() << 32) + ((long)readInt() & 0xffffffffL); } public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } public final short readShort() throws IOException { int i = read(); int j = read(); if((i | j) < 0) throw new EOFException(); else return (short)((i << 8) + j); } public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } public void close() throws IOException { //ij.IJ.log("close: "+(data!=null?""+data.size():"")); if (ras!=null) ras.close(); else { data.removeAllElements(); src.close(); } } }