// Copied from Slipstream Mod Manager 1.4.
package net.vhati.ftldat;
import java.io.InputStream;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
public class FileChannelRegionInputStream extends InputStream {
private FileChannel channel;
private long regionOffset;
private long regionLength;
// A buffer holds an even narrower region of the file.
// When possible read() calls will reuse this,
// rather than pester the channel.
private ByteBuffer buf = null;
private long bufOffset = 0; // Relative to regionOffset.
private int bufLength = 0;
private long intraPos = 0;
public FileChannelRegionInputStream( FileChannel channel, long offset, long length ) {
this( channel, offset, length, 4096 );
}
public FileChannelRegionInputStream( FileChannel channel, long offset, long length, int bufferSize ) {
this.channel = channel;
this.regionOffset = offset;
this.regionLength = length;
buf = ByteBuffer.allocate( bufferSize );
}
@Override
public int available() throws IOException {
if ( !channel.isOpen() ) throw new ClosedChannelException();
return bufLength;
}
@Override
public int read() throws IOException {
if ( !channel.isOpen() ) throw new ClosedChannelException();
if ( intraPos >= regionLength ) return -1;
if ( intraPos < bufOffset || intraPos >= bufOffset+bufLength ) {
// The requested byte isn't currently buffered.
bufOffset = intraPos;
int len = 0; // Get *something*.
buf.position( 0 );
while ( len == 0 ) {
len = channel.read( buf, regionOffset + bufOffset );
}
if ( len == -1 ) {
bufLength = 0;
return -1;
} else {
bufLength = len;
}
}
// Do an absolute get() from the buffer,
// and interpret the byte as if it were unsigned.
int result = (int)(buf.get( (int)(intraPos - bufOffset) ) & 0xff);
intraPos++;
return result;
}
@Override
public int read( byte[] b, int bOff, int bLen ) throws IOException {
if ( bLen == 0 ) return 0;
if ( bOff < 0 ) throw new IndexOutOfBoundsException( String.format( "Index: %d, Size: %d", bOff, bLen ) );
if ( bOff + bLen > b.length ) throw new IndexOutOfBoundsException( String.format( "Index: %d, Size: %d", (bOff+bLen), bLen ) );
if ( !channel.isOpen() ) throw new ClosedChannelException();
if ( intraPos >= regionLength ) return -1;
int bytesTotal = Math.min( bLen, (int)(regionLength - intraPos) );
int bytesRemaining = bytesTotal;
int bytesRead = 0;
if ( intraPos >= bufOffset && intraPos < bufOffset+bufLength ) {
// Read part of the current buffer, possibly until the end.
buf.position( (int)(intraPos - bufOffset) );
int bufTodo = Math.min( bytesRemaining, bufLength - (int)(intraPos - bufOffset) );
buf.get( b, bOff, bufTodo );
bytesRemaining -= bufTodo;
bytesRead += bufTodo;
intraPos += bufTodo;
}
if ( bytesRemaining > 0 ) {
// Refill the buffer at the current intraPos.
bufOffset = intraPos;
int len = 0;
buf.position( 0 );
len = channel.read( buf, regionOffset + bufOffset );
if ( len == -1 ) {
bufLength = 0;
throw new BufferUnderflowException();
} else {
bufLength = len;
}
buf.position( 0 );
int bufTodo = Math.min( bytesRemaining, bufLength );
buf.get( b, bOff + bytesRead, bufTodo );
bytesRemaining -= bufTodo;
bytesRead += bufTodo;
intraPos += bufTodo;
}
return bytesRead;
}
@Override
public long skip( long n ) throws IOException {
if ( !channel.isOpen() ) throw new ClosedChannelException();
if ( intraPos >= regionLength ) return -1;
if ( intraPos + n <= regionLength ) {
n = regionLength - intraPos;
}
intraPos += n;
return n;
}
}