mediautil.gen.directio.OutStreamToIterativeReader Maven / Gradle / Ivy
Go to download
SDK for dev_appserver (local development) with some of the dependencies shaded (repackaged)
/* MediaUtil LLJTran - $RCSfile: OutStreamToIterativeReader.java,v $
* Copyright (C) 1999-2005 Dmitriy Rogatkin, Suresh Mahalingam. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* $Id: OutStreamToIterativeReader.java,v 1.4 2005/08/18 04:35:34 drogatkin Exp $
*
*/
package mediautil.gen.directio;
import java.io.*;
/**
* This class enables writing directly to an IterativeReader which will read the
* data written as an InputStream. The write calls to this class which is to be
* used as an OutputStream translate into nextRead() calls on the underlying
* IterativeReader after buffering. The only catch is that the IterativeReader
* in this case should not exceed the read request beyond the readCushion as
* this will result in an empty buffer which will be flagged as an IOException
* on the IterativeReader's InputStream.
* @see IterativeReader
*
* @author Suresh Mahalingam ([email protected])
*/
public class OutStreamToIterativeReader extends OutputStream {
private static byte UNINITIALISED = 0;
private static byte OPEN = 1;
private static byte CLOSED = 2;
private boolean isDetached;
/** Internal Queue to hold bytes written */
protected byte q[];
/** Internal Queue variables */
protected int qBegin, qEnd, qSize;
private int minReadSize, readCushion;
private IterativeReader reader;
private final static int DEF_BUF_SIZE = 3072;
private byte openFlag = UNINITIALISED;
private byte oneByteArr[] = new byte[1];
private ReaderInputStream readerStream;
private class ReaderInputStream extends InputStream implements ByteCounter
{
private byte oneByteArr[] = new byte[1];
public byte writeBuf[];
public int writeBufPos, writeBufRemain = 0;
private int counterArr[];
private boolean upMode;
private long totalBytes = 0;
public void setCounter(int counterArr[], boolean upMode)
{
int i = counterArr[0]; // Good if an exception is detected
this.counterArr = counterArr;
this.upMode = upMode;
}
public long getTotalBytes()
{
return totalBytes;
}
public void setRequestBuf(byte b[], int off, int len)
{
writeBuf = b;
writeBufPos = off;
writeBufRemain = len;
}
private int readOrSkip(byte b[], int off, int len) throws IOException
{
if(openFlag == UNINITIALISED || isDetached)
throw new IOException("IterativeReader not set or has been detached");
int qReadSize = qSize;
int remain;
if(qReadSize > len)
qReadSize = len;
// dequeue(q, b, off, qReadSize)
if(qReadSize > 0)
{
remain = q.length - qBegin;
if(remain > qReadSize)
remain = qReadSize;
if(b != null)
{
System.arraycopy(q, qBegin, b, off, remain);
off += remain;
}
qBegin += remain;
if(qBegin == q.length)
qBegin = 0;
remain = qReadSize - remain;
if(remain > 0)
{
if(b != null)
{
System.arraycopy(q, qBegin, b, off, remain);
off += remain;
}
qBegin += remain;
}
qSize -= qReadSize;
if(qSize == 0)
{
qBegin = 0;
qEnd = 0;
}
}
// End dequeue
remain = len - qReadSize;
if(remain > 0)
{
if(remain > writeBufRemain)
{
if(openFlag == CLOSED)
remain = writeBufRemain>0?writeBufRemain:0;
else if(b != null)
throw new IOException("Iterative Reader attempting to read beyond buffer end. Need larger read cushion?");
}
if(b != null)
System.arraycopy(writeBuf, writeBufPos, b, off, remain);
writeBufPos += remain;
writeBufRemain -= remain;
}
int retVal = qReadSize + remain;
if(retVal > 0)
{
totalBytes += retVal;
if(counterArr != null)
{
if(upMode)
counterArr[0] += retVal;
else
counterArr[0] -= retVal;
}
}
return retVal;
}
public int read(byte b[], int off, int len) throws IOException
{
if(len < 0)
throw new IndexOutOfBoundsException("Negative Length Read attempted, len = " + len);
byte b1 = b[off], b2 = b[off + len -1];
int retVal = readOrSkip(b, off, len);
if(retVal == 0)
retVal = -1;
return retVal;
}
public long skip(long n) throws IOException
{
long retVal = 0;
if(n < 0)
n = 0;
retVal = readOrSkip(null, 0, (int)n);
return retVal;
}
public int read() throws IOException
{
int retVal = -1;
if(read(oneByteArr) == 1)
retVal = (oneByteArr[0] & 255);
return retVal;
}
/**
* Does not strictly conform to InputStream spec since it always returns
* atleast 1 unless the end of file is reached.
**/
public int available() throws IOException
{
int retVal = qSize;
if(retVal < 1 && openFlag != CLOSED)
retVal = 1;
return retVal;
}
public void close() throws IOException
{
isDetached = true;
q = null;
}
}
/**
* Creates an OutStreamToIterativeReader Object. This Object can be used as
* an OutputStream. However an IterativeReader must be set before using the
* stream.
* @param bufSize Buffer Size
* @param minReadSize Minimum Read request size for the IterativeRead's
* nextRead() call
* @param readCushion Bytes by which the actual bytes read may exceed the
* requested number of bytes for the ItertativeReader's nextRead() call. If
* the nextRead() call overshoots this limit also then an empty buffer
* results which is flagged as an IOException for the reader's InputStream.
*/
public OutStreamToIterativeReader(int bufSize, int minReadSize,
int readCushion)
{
if(minReadSize <= 0)
minReadSize = 1;
if(readCushion <= 0)
readCushion = 1;
int minBufSize = minReadSize + readCushion + 512;
if(bufSize < minBufSize)
bufSize = minBufSize;
q = new byte[bufSize];
qBegin = 0;
qEnd = 0;
qSize = 0;
this.minReadSize = minReadSize;
this.readCushion = readCushion;
isDetached = false;
}
/**
* Creates an OutStreamToIterativeReader Object. This Object can be used as
* an OutputStream. However an IterativeReader must be set before using the
* stream. Uses a a buffer size of 3072, and a minReadSize and readCushion
* of 1024 bytes.
* @see #OutStreamToIterativeReader(int,int,int)
*/
public OutStreamToIterativeReader()
{
this(DEF_BUF_SIZE, 1024, 1024);
}
/**
* Gets the InputStream for use by the IterativeReader. This class's write
* method fills the buffer for use by this InputStream's read. Closing
* this stream is equivalent to the nextRead of the iterativeReader
* returning IterativeReader.STOP, in which case IterativeReader is detached
* and subsequent writes to the OutStreamToIterativeReader Object are
* ignored.
* @return Stream for use by IterativeReader. Note that the returned Object
* implements {@link ByteCounter} and can be cast to a ByteCounter for
* the IterativeReader to keep track of the number of bytes read.
* @see #setIterativeReader(IterativeReader)
*/
public InputStream getReaderInputStream()
{
if(readerStream == null)
readerStream = new ReaderInputStream();
if(reader != null &&
openFlag == UNINITIALISED)
openFlag = OPEN;
return readerStream;
}
/**
* Sets the IterativeReader for this OutStreamToIterativeReader. This must
* be called before writing data to this stream.
* @param iterativeReader IterativeReader to call to read data. The
* iterativeReader must write to the InputStream got by the
* getReaderInputStream() call.
* @see #getReaderInputStream()
*/
public void setIterativeReader(IterativeReader iterativeReader)
{
if(iterativeReader == null)
throw new NullPointerException("Reader is null");
if(readerStream != null &&
openFlag == UNINITIALISED)
openFlag = OPEN;
reader = iterativeReader;
}
/**
* Write method of the OutStreamToIterativeReader.
* See the documentation of OutputStream.write(..) for more information.
* This writes data to fill the internal buffer and the calls the
* iterativeReader's nextRead metho to read the buffered data.
* @exception IOException If the nextRead() call of the iterativeReader
* throws an IOException
* @see IterativeReader#nextRead(int)
*/
public void write(byte b[], int off, int len) throws IOException
{
if(openFlag != OPEN)
throw new IOException("Stream is closed or IterativeReader not set yet");
int excessSkip = readerStream.writeBufRemain;
if(excessSkip < 0)
{
len += excessSkip;
off -= excessSkip;
excessSkip = len>=0?0:len;
readerStream.writeBufRemain = excessSkip;
}
detached:
if(len > 0)
{
// Do not do anything if readerStream detached
if(isDetached)
break detached;
int curSize = qSize + len;
if(curSize > q.length)
{
int readSize = curSize - readCushion;
readerStream.writeBuf = b;
readerStream.writeBufPos = off;
readerStream.writeBufRemain = len;
do
if(reader.nextRead(readSize) !=
IterativeReader.CONTINUE)
{
readerStream.close();
break detached;
}
while( (readSize = qSize + readerStream.writeBufRemain -
readCushion) > minReadSize);
off = readerStream.writeBufPos;
len = readerStream.writeBufRemain;
readerStream.writeBufRemain = 0;
}
// enqueue(q, b, off, len);
if(len > 0)
{
int remain = q.length - qEnd;
if(remain > len)
remain = len;
System.arraycopy(b, off, q, qEnd, remain);
off += remain;
qEnd += remain;
if(qEnd >= q.length)
qEnd = 0;
remain = len - remain;
if(remain > 0)
{
System.arraycopy(b, off, q, qEnd, remain);
qEnd += remain;
}
qSize += len;
}
}
}
/**
* Write method of the OutStreamToIterativeReader.
* See the documentation of OutputStream.write(..) for more information.
* This writes data to fill the internal buffer and the calls the
* iterativeReader's nextRead method to read the buffered data.
* @exception IOException If the nextRead() call of the iterativeReader
* throws an IOException
* @see IterativeReader#nextRead(int)
*/
public void write(int b) throws IOException
{
oneByteArr[0] = (byte)(b & 255);
write(oneByteArr, 0, 1);
}
/**
* Closes the OutStreamToIterativeReader.
* See the documentation of OutputStream.write(..) for more information.
* This flags the end of the internal buffer data as end of file. Then it
* calls the iterativeReader's nextRead method till it returns
* IterativeReader.STOP.
* @exception IOException If the nextRead() call of the iterativeReader
* throws an IOException
* @see IterativeReader#nextRead(int)
*/
public void close() throws IOException
{
openFlag = CLOSED;
int curSize;
if(!isDetached)
while( (curSize = qSize + readerStream.writeBufRemain) > 0)
if(reader.nextRead(curSize) != IterativeReader.CONTINUE)
{
readerStream.close();
break;
}
}
/**
* Returns if the IterativeReader attached to this
* OutStreamToIterativeReader has detached.
* @return True if the reader has detached, false otherwise.
*/
public boolean isReaderDetached()
{
return isDetached;
}
}