package org.limewire.io;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import org.limewire.util.BufferUtils;
/**
* Wraps {@link ByteBuffer ByteBuffers} so it can be accessed as an
* {@link InputStream}. <code>ByteBufferInputStream</code> contains an internal
* buffer that contains bytes that may be read from the stream.
* <code>ByteBufferInputStream</code> is similar to {@link ByteArrayInputStream},
* however it uses <code>ByteBuffer</code>.
* <p>
* <code>ByteBufferInputStream</code> is not synchronized.
*/
public class ByteBufferInputStream extends InputStream {
/** An empty ByteBuffer */
private static final ByteBuffer[] EMPTY = new ByteBuffer[0];
/** The index of the current ByteBuffer */
protected int index = 0;
/** The index of the marked ByteBuffer */
protected int mark = -1;
/** The array of ByteBuffer(s) we're reading from */
protected ByteBuffer[] buffers;
/**
* Creates a ByteBufferInputStream that is reading from
* one or more ByteBuffer(s).
*/
public ByteBufferInputStream(ByteBuffer... buffers) {
this.buffers = buffers;
}
/**
* Returns the underlying array of ByteBuffer(s).
*/
public ByteBuffer[] getBuffers() {
if (buffers == EMPTY) {
throw new IllegalStateException("InputStream is closed");
}
return buffers;
}
@Override
public int available() {
int available = 0;
for(int i = buffers.length-1; i >= index; --i) {
available += buffers[i].remaining();
}
return available;
}
@Override
public void close() {
index = 0;
mark = -1;
buffers = EMPTY;
}
@Override
public void mark(int readlimit) {
if (index < buffers.length) {
mark = index;
for(int i = buffers.length-1; i >= mark; --i) {
buffers[i].mark();
}
}
}
@Override
public void reset() {
if (mark != -1) {
index = mark;
for(int i = buffers.length-1; i >= index ; --i) {
buffers[i].reset();
}
mark = -1;
}
}
@Override
public boolean markSupported() {
return true;
}
@Override
public int read() {
while(index < buffers.length) {
ByteBuffer b = buffers[index];
if (b.hasRemaining()) {
return b.get() & 0xFF;
}
// Try next ByteBuffer
++index;
}
return -1;
}
@Override
public int read(byte[] b) {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0)
|| ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (index >= buffers.length) {
return -1;
} else if (index == buffers.length-1
&& !buffers[index].hasRemaining()) {
return -1;
} else if (len == 0 || b.length == 0) {
return (available() > 0 ? 0 : -1);
}
int r = 0;
while(index < buffers.length) {
ByteBuffer buf = buffers[index];
// The number of bytes we can read
int l = Math.min(buf.remaining(), len-r);
// Do read
buf.get(b, off+r, l);
r += l;
// Exit if done
if (r == len) {
break;
}
// Try next ByteBuffer
++index;
}
// Return EOF if no bytes were available
return (r > 0 ? r : -1);
}
@Override
public long skip(long n) {
long s = 0L;
while(index < buffers.length) {
ByteBuffer b = buffers[index];
// The number of bytes we can skip in this ByteBuffer
long l = Math.min(b.remaining(), n-s);
b.position(b.position() + (int)l);
s += l;
// Exit if done
if (s == n) {
break;
}
// Try next ByteBuffer
++index;
}
return s;
}
/** Reads as much as possible into the destination buffer. */
public long read(ByteBuffer dst) {
long read = 0L;
while(index < buffers.length) {
ByteBuffer b = buffers[index];
if(b.hasRemaining())
read += BufferUtils.transfer(b, dst, false);
if(!dst.hasRemaining())
break;
// Try next ByteBuffer
++index;
}
return read;
}
/**
* Returns a reference to the existing buffers if possible.
* If not possible, returns a copy of the data in a new buffer.
* <p>
* This advances the read mark, as if the requested data has
* been read.
* <p>
* If there wasn't enough data to place into the buffer, the
* buffer is created with as much data as possible.
*/
public ByteBuffer bufferFor(int length) {
ByteBuffer ret = null;
// make sure we advance to the first buffer with data left.
// this lets the reference work correctly.
while(index < buffers.length) {
if(buffers[index].hasRemaining())
break;
index++;
}
if(index < buffers.length) {
length = Math.min(length, available());
ByteBuffer b = buffers[index];
if(b.remaining() >= length ) {
int oldLimit = b.limit();
b.limit(b.position() + length);
ret = b.slice();
b.limit(oldLimit);
b.position(b.position() + length);
} else {
ret = ByteBuffer.allocate(length);
read(ret);
ret.flip();
}
} else {
ret = BufferUtils.getEmptyBuffer();
}
return ret;
}
}