// 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.OutputStream;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
/**
* A simple OutputStream implementation which uses one or more {@link ByteBuffer} objects as backend.
*/
public class ByteBufferOutputStream extends OutputStream
{
private final ByteBuffer[] bufs;
private int curBuf;
public ByteBufferOutputStream(ByteBuffer buf)
{
this(new ByteBuffer[]{buf});
}
public ByteBufferOutputStream(ByteBuffer... bufs)
{
if (bufs == null) {
throw new NullPointerException();
}
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 void write(int b) throws IOException
{
if (!isOpen()) {
throw new IOException("Stream not open");
}
ByteBuffer buf = getBuffer(true);
if (!buf.hasRemaining()) {
throw new BufferOverflowException();
}
buf.put((byte)b);
}
@Override
public void write(byte[] bytes, int off, int len) throws IOException
{
if (bytes == null) {
throw new NullPointerException();
}
if(off < 0 || len < 0 || off + len > bytes.length) {
throw new IndexOutOfBoundsException();
}
if (!isOpen()) {
throw new IOException("Stream not open");
}
int written = 0;
while (written < len) {
ByteBuffer buf = getBuffer(true);
int remaining = Math.min(buf.remaining(), len - written);
if (remaining <= 0) {
throw new IndexOutOfBoundsException();
}
buf.put(bytes, written, remaining);
written += remaining;
}
}
@Override
public void close() throws IOException
{
if (curBuf >= 0) {
for (final ByteBuffer buf: bufs) {
if (buf instanceof MappedByteBuffer) {
((MappedByteBuffer)buf).force();
}
}
synchronized (this) {
curBuf = -1;
}
}
}
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();
}
return bufs[curBuf];
}
return null;
}
}