package net.i2p.util;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* Simple lookahead buffer to keep the last K bytes in reserve,
* configured to easily be reused. Currently only used by the
* ResettableGZIPInputStream
*
*/
public class LookaheadInputStream extends FilterInputStream {
private boolean _eofReached;
private final byte[] _footerLookahead;
private static final InputStream _fakeInputStream = new ByteArrayInputStream(new byte[0]);
/**
* Configure a stream that hides a number of bytes from the reader.
* The last n bytes will never be available from read(),
* they can only be obtained from getFooter().
*
* initialize() MUST be called before doing any read() calls.
*
* @param lookaheadSize how many bytes to hide
*/
public LookaheadInputStream(int lookaheadSize) {
super(_fakeInputStream);
_footerLookahead = new byte[lookaheadSize];
}
public boolean getEOFReached() { return _eofReached; }
/**
* Start the LookaheadInputStream with the given input stream.
* Resets everything if the LookaheadInputStream was previously used.
* WARNING - blocking until lookaheadSize bytes are read!
*/
public void initialize(InputStream src) throws IOException {
in = src;
_eofReached = false;
Arrays.fill(_footerLookahead, (byte)0x00);
int footerRead = 0;
while (footerRead < _footerLookahead.length) {
int read = in.read(_footerLookahead, footerRead, _footerLookahead.length - footerRead);
if (read == -1) throw new IOException("EOF reading the footer lookahead");
footerRead += read;
}
}
@Override
public int read() throws IOException {
if (_eofReached)
return -1; //throw new IOException("Already past the EOF");
int c = in.read();
if (c == -1) {
_eofReached = true;
return -1;
}
int rv = _footerLookahead[0];
// FIXME use an index!!!!!!!!!!!!
System.arraycopy(_footerLookahead, 1, _footerLookahead, 0, _footerLookahead.length-1);
_footerLookahead[_footerLookahead.length-1] = (byte)c;
if (rv < 0) rv += 256;
return rv;
}
@Override
public int read(byte buf[]) throws IOException {
return read(buf, 0, buf.length);
}
@Override
public int read(byte buf[], int off, int len) throws IOException {
if (_eofReached)
return -1;
for (int i = 0; i < len; i++) {
int c = read();
if (c == -1) {
if (i == 0)
return -1;
else
return i;
} else {
buf[off+i] = (byte)c;
}
}
return len;
}
/** grab the lookahead footer */
public byte[] getFooter() { return _footerLookahead; }
/*******
public static void main(String args[]) {
byte buf[] = new byte[32];
for (int i = 0; i < 32; i++)
buf[i] = (byte)i;
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
try {
LookaheadInputStream lis = new LookaheadInputStream(8);
lis.initialize(bais);
byte rbuf[] = new byte[32];
int read = lis.read(rbuf);
if (read != 24) throw new RuntimeException("Should have stopped (read=" + read + ")");
for (int i = 0; i < 24; i++)
if (rbuf[i] != (byte)i)
throw new RuntimeException("Error at " + i + " [" + rbuf[i] + "]");
for (int i = 0; i < 8; i++)
if (lis.getFooter()[i] != (byte)(i+24))
throw new RuntimeException("Error at footer " + i + " [" + lis.getFooter()[i] + "]");
System.out.println("Everything is fine in general");
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 9; i < 32*1024; i++) {
if (!test(i)) break;
}
}
private static boolean test(int size) {
byte buf[] = new byte[size];
new java.util.Random().nextBytes(buf);
ByteArrayInputStream bais = new ByteArrayInputStream(buf);
try {
LookaheadInputStream lis = new LookaheadInputStream(8);
lis.initialize(bais);
byte rbuf[] = new byte[size];
int read = lis.read(rbuf);
if (read != (size-8)) throw new RuntimeException("Should have stopped (read=" + read + ")");
for (int i = 0; i < (size-8); i++)
if (rbuf[i] != buf[i])
throw new RuntimeException("Error at " + i + " [" + rbuf[i] + "]");
for (int i = 0; i < 8; i++)
if (lis.getFooter()[i] != buf[i+(size-8)])
throw new RuntimeException("Error at footer " + i + " [" + lis.getFooter()[i] + "]");
System.out.println("Everything is fine at size=" + size);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
******/
}