// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.util.io;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* A simple InputStream implementation which uses one or more {@link ByteBuffer} objects as backend.
*/
public class ByteBufferInputStream extends InputStream
{
private final ByteBuffer[] bufs;
private int curBuf;
private int markBufIndex, markBufPosition;
public ByteBufferInputStream(ByteBuffer buf)
{
this(new ByteBuffer[]{buf});
}
public ByteBufferInputStream(ByteBuffer... bufs)
{
if (bufs == null) {
throw new NullPointerException();
}
this.markBufIndex = -1;
this.markBufPosition = -1;
if (bufs.length > 0) {
this.bufs = new ByteBuffer[bufs.length];
for (int i = 0; i < bufs.length; i++) {
if (bufs[i] == null) {
throw new NullPointerException();
}
this.bufs[i] = bufs[i];
}
this.curBuf = 0;
} else {
this.bufs = null;
this.curBuf = -1;
}
}
@Override
public int read() throws IOException
{
if (!isOpen()) {
throw new IOException("Stream not open");
}
ByteBuffer buf = getBuffer(true);
if (!buf.hasRemaining()) {
return -1;
}
return buf.get() & 0xff;
}
@Override
public int read(byte[] bytes, int off, int len) throws IOException
{
if (bytes == null) {
throw new NullPointerException();
}
if (!isOpen()) {
throw new IOException("Stream not open");
}
int read = 0;
while (read < len) {
ByteBuffer buf = getBuffer(true);
if (buf == null) {
break;
}
int remaining = Math.min(buf.remaining(), len - read);
if (remaining <= 0) {
break;
}
buf.get(bytes, read, remaining);
read += remaining;
}
return (read > 0) ? read : -1;
}
@Override
public long skip(long n) throws IOException
{
if (n <= 0) {
return 0;
}
byte[] tmp = new byte[Math.min(1024, (int)n)];
int read = 0;
while (read < n) {
int remaining = Math.min(tmp.length, (int)n - read);
int n2 = read(tmp, 0, remaining);
if (n2 < 0) {
break;
}
read += n2;
}
return read;
}
@Override
public int available() throws IOException
{
if (isOpen() && curBuf < bufs.length) {
int idx = curBuf;
int sum = bufs[idx++].remaining();
for (int i = idx; i < bufs.length; i++) {
sum += bufs[i].remaining();
}
return sum;
}
throw new IOException("Stream is closed");
}
@Override
public void close() throws IOException
{
if (curBuf >= 0) {
synchronized (this) {
curBuf = -1;
}
}
}
@Override
public boolean markSupported()
{
return true;
}
@Override
public synchronized void mark(int readlimit)
{
if (isOpen()) {
ByteBuffer buffer = getBuffer(true);
if (buffer != null) {
markBufIndex = curBuf;
markBufPosition = buffer.position();
} else {
markBufIndex = -1;
markBufPosition = -1;
}
}
}
@Override
public synchronized void reset() throws IOException
{
if (isOpen() && markBufIndex >= 0 && markBufIndex < bufs.length && markBufPosition >= 0) {
curBuf = markBufIndex;
bufs[curBuf].position(markBufPosition);
markBufIndex = -1;
markBufPosition = -1;
} else {
throw new IOException();
}
}
private boolean isOpen()
{
return (curBuf >= 0);
}
private void updateBuffer()
{
if (isOpen()) {
if (curBuf < bufs.length) {
if (bufs[curBuf].remaining() <= 0) {
synchronized (this) {
curBuf++;
}
}
}
}
}
private ByteBuffer getBuffer(boolean update)
{
if (isOpen()) {
if (update) {
updateBuffer();
}
if (curBuf < bufs.length) {
return bufs[curBuf];
}
}
return null;
}
}