mediautil.gen.directio.SplitInputStream Maven / Gradle / Ivy
/* MediaUtil LLJTran - $RCSfile: SplitInputStream.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: SplitInputStream.java,v 1.4 2005/08/18 04:35:34 drogatkin Exp $
*
*/
package mediautil.gen.directio;
import java.io.*;
import mediautil.gen.BasicIo;
/**
* This class enables the Sharing of an InputStream (Say from a file) by One or
* more IterativeReaders with a main reader. The main reader is any reader like
* say an ImageReader for reading images.
*
* This is accomplished by using a common buffer. The read/skip calls on this
* class by the main reader calls nextRead() on the SubStreams to consume the
* buffered data before the buffer is filled again.
* @see IterativeReader
*
* @author Suresh Mahalingam ([email protected])
*/
public class SplitInputStream extends FilterInputStream {
private byte q[];
private final static int DEF_BUF_SIZE = 5120;
private final static int DEF_INC_SIZE = 1024;
private byte oneByteArr[] = new byte[1];
private int bufSize, incSize, qEnd, q2nd;
private boolean eofFlag;
private int qPtrs[];
private int subReaderIds[];
private IterativeReader codes[];
private SubStream subStreams[];
private byte requestBuf[];
private int requestPos, requestRemain;
private int curReaderId;
private int maxReaderId;
private int maxBufSize, maxBufUsage;
private class SubStream extends InputStream implements ByteCounter
{
public SplitInputStream parentThis;
private byte oneByteArr[] = new byte[1];
private int readerNo, readerId;
public int minReadSize, readCushion;
public boolean closedFlag;
private int counterArr[];
private boolean upMode;
private long totalBytes;
public SubStream(SplitInputStream parentThis, int minReadSize,
int readCushion)
{
this.parentThis = parentThis;
this.readCushion = readCushion;
this.minReadSize = minReadSize;
closedFlag = false;
readerNo = -1;
readerId = 0;
totalBytes = 0;
counterArr = null;
}
public SubStream(SplitInputStream parentThis)
{
this(parentThis, 750, 750);
}
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;
}
private String validate()
{
String retVal = null;
if(!closedFlag)
{
if(readerNo < 0)
retVal = "Attempt to read from an invalid or unattached substream";
else if(readerId != curReaderId)
retVal = "Substream Context Error: Iterative code attached as id " + curReaderId + " attempting to read from substream with id " + readerId;
}
return retVal;
}
public int read(byte b[], int off, int len) throws IOException
{
// String ioError = validate(); Inlining validate for performance
String ioError = null;
if(!closedFlag)
{
if(readerNo < 0)
ioError = "Attempt to read from an invalid or unattached substream";
else if(readerId != curReaderId)
ioError = "Substream Context Error: Iterative code attached as id " + curReaderId + " attempting to read from substream with id " + readerId;
if(ioError != null)
throw new IOException(ioError);
}
if(len < 0)
throw new IndexOutOfBoundsException("Negative Length Read attempted, len = " + len);
byte b1 = b[off], b2 = b[off + len -1];
if(len == 0)
return 0;
if(closedFlag)
return -1;
if(q == null)
q = new byte[bufSize];
// Begin q.dequeue(in, readerNo, b, off, len)
int remain = len;
int qReadSize = qEnd - qPtrs[readerNo];
if(qReadSize > len)
qReadSize = len;
if(qReadSize > 0)
{
System.arraycopy(q, qPtrs[readerNo], b, off, qReadSize);
qPtrs[readerNo] += qReadSize;
off += qReadSize;
remain -= qReadSize;
}
int retVal = len;
if(remain > 0)
{
if(!eofFlag)
remain -= fillAndDequeue(readerNo, b, off, remain);
retVal -= remain;
if(retVal <= 0)
{
retVal = -1;
detachSubReaderNo(readerNo-1);
}
}
// End q.dequeue(in, readerNo, b, off, len)
if(retVal > 0)
{
totalBytes += retVal;
if(counterArr != null)
{
if(upMode)
counterArr[0] += retVal;
else
counterArr[0] -= retVal;
}
}
return retVal;
}
public int read() throws IOException
{
int retVal = -1;
if(read(oneByteArr, 0, 1) == 1)
retVal = oneByteArr[0] & 255;
return retVal;
}
/**
* Does not strictly conform to InputStream spec since it always returns
* n unless it is eof.
**/
public long skip(long n) throws IOException
{
String ioError = validate();
if(ioError != null)
throw new IOException(ioError);
int retVal = (int)n;
if(closedFlag || retVal < 0)
retVal = 0;
qPtrs[readerNo] += retVal;
totalBytes += retVal;
if(counterArr != null)
{
if(upMode)
counterArr[0] += retVal;
else
counterArr[0] -= retVal;
}
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
{
String ioError = validate();
if(ioError != null)
throw new IOException(ioError);
int retVal = 0;
if(!closedFlag)
{
retVal = qEnd - qPtrs[readerNo];
if(retVal <= 0)
retVal = eofFlag?0:1;
}
return retVal;
}
public void close() throws IOException
{
String ioError = validate();
if(ioError != null)
throw new IOException(ioError);
if(readerNo > 0)
detachSubReaderNo(readerNo-1);
}
}
// This is inlined in read calls to improve performance. Is it going to?
private int dequeue(int readerNo, byte b[], int off, int n)
{
int qReadSize = qEnd - qPtrs[readerNo];
if(qReadSize > n)
qReadSize = n;
if(qReadSize > 0)
{
if(b != null)
System.arraycopy(q, qPtrs[readerNo], b, off, qReadSize);
qPtrs[readerNo] += qReadSize;
}
else
qReadSize = 0;
return qReadSize;
}
private void reallocBuf(int newLen)
{
int offset = findQBegin(null);
byte newQ[] = new byte[newLen];
int copyLen = qEnd - offset;
if(copyLen > 0)
System.arraycopy(q, offset, newQ, 0, copyLen);
else
offset = qEnd;
q = newQ;
int i;
for(i = 0; i < qPtrs.length; ++i)
qPtrs[i] -= offset;
qEnd -= offset;
q2nd -= offset;
if(newLen > maxBufSize)
maxBufSize = newLen;
}
private int fillAndDequeue(int readerNo, byte b[], int off, int len)
throws IOException
{
// Caller would have done basic dequeue, but still to be sure.
int qReadSize = dequeue(readerNo, b, off, len);
int remain = len - qReadSize;
off += qReadSize;
int qPtr = qPtrs[readerNo];
int i;
// Real Tough fillAndDequeue
if(remain > 0 && !eofFlag)
{
// Since dequeue done skipLen >= 0
int skipLen = qPtr - qEnd;
int fillSize = remain + skipLen;
int reqSize = fillSize + (qEnd - q2nd);
int readLen, actualRead;
if(q2nd < qPtr)
skipLen = q2nd - qEnd;
if(q2nd > qEnd && fillSize > bufSize
&& (reqSize <= bufSize || (reqSize - bufSize -1)/incSize <
(fillSize - bufSize -1)/incSize))
{
int newQStart = qPtr + remain;
if(newQStart > q2nd)
newQStart = q2nd;
int dFillLen = newQStart - qEnd;
int dRemain = dFillLen;
readLen = 0;
if(requestBuf != null)
{
readLen = requestRemain;
if(readLen > dRemain)
readLen = dRemain;
}
if(readLen > 0)
{
actualRead = BasicIo.read(in, requestBuf, requestPos, readLen,
readLen);
if(actualRead < readLen)
eofFlag = true;
int xferLen = actualRead - skipLen;
if(xferLen > 0)
{
if(b != null)
System.arraycopy(requestBuf, requestPos + skipLen,
b, off, xferLen);
skipLen = 0;
off += xferLen;
remain -= xferLen;
}
else
skipLen = -xferLen;
dRemain -= actualRead;
}
if(skipLen > 0 && !eofFlag)
{
actualRead = (int) BasicIo.skip(in, skipLen);
if(actualRead < skipLen)
eofFlag = true;
skipLen -= actualRead;
dRemain -= actualRead;
}
if(dRemain > 0 && !eofFlag)
{
if(b!= null)
actualRead = BasicIo.read(in, b, off, dRemain, dRemain);
else
actualRead = (int) BasicIo.skip(in, dRemain);
if(actualRead < dRemain)
eofFlag = true;
off += actualRead;
remain -= actualRead;
dRemain -= actualRead;
}
readLen = dFillLen - dRemain;
qEnd += readLen;
if(readLen > requestRemain)
readLen = requestRemain;
requestPos += readLen;
requestRemain -= readLen;
qPtr += len - qReadSize - remain;
}
if(requestBuf == null && skipLen > 0 && !eofFlag)
{
actualRead = (int) BasicIo.skip(in, skipLen);
if(actualRead < skipLen)
eofFlag = true;
readLen = requestRemain;
if(readLen > actualRead)
readLen = actualRead;
requestPos += readLen;
requestRemain -= readLen;
qEnd += actualRead;
}
qPtrs[readerNo] = qPtr;
if(remain > 0 && !eofFlag)
{
int newQBegin = q2nd;
if(qEnd < q2nd)
newQBegin = qEnd;
fillSize = remain + (qPtr - qEnd);
reqSize = fillSize + (qEnd - newQBegin);
if(reqSize > maxBufUsage)
maxBufUsage = reqSize;
if(reqSize > q.length)
reallocBuf(reqSize + incSize - 1 -
(reqSize - 1 - bufSize)%incSize);
else
{
if(qEnd > newQBegin && newQBegin > 0)
System.arraycopy(q, newQBegin, q, 0, qEnd - newQBegin);
i = 0;
do
{
qPtrs[i] -= newQBegin;
++i;
} while(i < qPtrs.length);
qEnd -= newQBegin;
q2nd -= newQBegin;
}
int maxFillSize = bufSize - qEnd;
if(maxFillSize < fillSize)
maxFillSize = fillSize;
actualRead = BasicIo.read(in, q, qEnd, fillSize, maxFillSize);
if(actualRead < fillSize)
eofFlag = true;
readLen = requestRemain;
if(readLen > actualRead)
readLen = actualRead;
if(readLen > 0)
{
if(requestBuf != null)
System.arraycopy(q, qEnd, requestBuf, requestPos, readLen);
requestPos += readLen;
requestRemain -= readLen;
}
qEnd += actualRead;
// Finally Dequeue bytes read
remain -= dequeue(readerNo, b, off, remain);
}
}
return len - remain;
}
private int findQBegin(int qBeginIndex[])
{
int i, qBegin, index = -1;
if(qPtrs.length == 0)
qBegin = 0;
else
{
qBegin = 0x7FFFFFFF;
i = 0;
do
if(qPtrs[i] < qBegin)
{
qBegin = qPtrs[i];
index = i;
}
while(++i < qPtrs.length);
}
if(qBeginIndex != null)
qBeginIndex[0] = index;
return qBegin;
}
private void init(InputStream mainStream, int bufSize, int incSize)
{
if(bufSize < 2048)
bufSize = 2048;
if(incSize < 512)
incSize = 512;
this.bufSize = bufSize;
maxBufSize = bufSize;
this.incSize = incSize;
qPtrs = new int[1];
qPtrs[0] = 0;
qEnd = 0;
q = null;
eofFlag = false;
subReaderIds = new int[0];
subStreams = new SubStream[0];
codes = new IterativeReader[0];
curReaderId = -1;
maxReaderId = 0;
}
/**
* Creates a new SplitInputStream Object. This can be used as an Input
* Stream.
* @param mainStream The main input Stream, say a FileInputStream. Note that
* this need not be buffered since SplitInputStream itself does buffering.
* The Main Object (Say ImageReader) will read from the newly constructed
* SplitInputStream object after attaching zero or more IterativeReaders.
* The SplitInputStream and Sub Streams see the same data as in mainStream.
* @param bufSize The Initial BufferSize. This will be increased if one of
* the Sub Readers read way beyond the requested Size
* @param incSize The buffer Size is always increase/decreased in multiples
* of incSize
*/
public SplitInputStream(InputStream mainStream, int bufSize, int incSize)
{
super(mainStream);
init(mainStream, bufSize, incSize);
}
/**
* Creates a new SplitInputStream Object with default parameters. The
* bufSize used is 5120 bytes, incSize used is 1024.
* @param mainStream The main input Stream, say a FileInputStream. Note that
* this need not be buffered since SplitInputStream itself does buffering.
* The Main Object (Say ImageReader) will read from the newly constructed
* SplitInputStream object after attaching zero or more IterativeReaders.
* The SplitInputStream and Sub Streams see the same data as in mainStream.
*/
public SplitInputStream(InputStream mainStream)
{
this(mainStream, DEF_BUF_SIZE, DEF_INC_SIZE);
}
/**
* Creates a Sub Stream for use by an instance of IterativeReader. The
* SubStream sees the same data as in the mainStream. The Buffer Size of the
* SplitInputStream if less than (minReadSize+readCushion+512) is resized to
* that value.
* @param minReadSize The minimum Number of Bytes that should be passed to
* the nextRead method of the IterativeReader. This value is limited to
* atleast 512.
* @param readCushion The maximum number of bytes by which a nextRead() call
* to the subReader may overshoot the requested number of bytes. If a
* nextRead() call exceeds the limit beyond readCushion a buffer expansion
* may result which affects performance. This value is limited to atleast
* 512.
* @return An InputStream which also implements ByteCounter which can be
* used by the IterativeReader to keep track of the bytes read/skipped. This
* Input Stream must be used directly (without creating a
* BufferedInputStream) by an Object implementing IterativeReader. The
* attachSubReader() method must be called with the IterativeReader Object
* and this return value after this call.
*
* Please note that the returned Input Stream does not strictly confirm to
* jdk's InputStream spec in the following:
*
* - The Skip call always skips the required number of bytes unless the
* end of file has already been detected. This is because for skip just
* the internal q pointers are incremented without actually checking for
* the end of file. This is to avoid possible unnecessary buffering of
* data.
*
- available always returns atleast 1 unless the end of file has been
* detected
*
* @see IterativeReader
* @see #attachSubReader(IterativeReader, InputStream)
* @see ByteCounter
*/
public InputStream createSubStream(int minReadSize, int readCushion)
{
if(minReadSize < 512)
minReadSize = 512;
if(readCushion < 512)
readCushion = 512;
return new SubStream(this, minReadSize, readCushion);
}
/**
* Creates a Sub Stream for use by an instance of IterativeReader. A minimum
* read Size of 750 and a read Cushion of 750 is used.
* @return An InputStream which also implements ByteCounter which can be
* used by the IterativeReader to keep track of the bytes read/skipped. This
* Input Stream must be used directly (without creating a
* BufferedInputStream) by an Object implementing IterativeReader. The
* attachSubReader() method must be called with the IterativeReader Object
* and this return value after this call.
*
* Please note that the returned Input Stream does not strictly confirm to
* jdk's InputStream spec in the following:
*
* - The Skip call always skips the required number of bytes unless the
* end of file has already been detected. This is because for skip just
* the internal q pointers are incremented without actually checking for
* the end of file. This is to avoid possible unnecessary buffering of
* data.
*
- available always returns atleast 1 unless the end of file has been
* detected
*
* @see IterativeReader
* @see #attachSubReader(IterativeReader,InputStream)
* @see ByteCounter
*/
public InputStream createSubStream()
{
return new SubStream(this);
}
/**
* This call must follow a createSubStream() call to attach an
* IterativeReader to read from the subStream created. This subReader is
* detached by a detachSubReader(..) call or when the nextRead() method
* returns IterativeReader.STOP or when the subStream is closed.
* @param code The IterativeReader to share the main Input. The nextRead()
* method is called on code by the read/skip calls on SplitInputStream as
* and when required.
* @param subStream The SubStream which was created using createSubStream()
* call and not yet attached. The nextRead() calls to the IterativeReader
* specified by the parameter code must read from this subStream.
* @return An integer representing a SubReader Id. This id should be passed
* if a stream is required to be detached.
* @see IterativeReader#nextRead(int)
* @see #createSubStream(int,int)
* @see #detachSubReader(int)
*/
public int attachSubReader(IterativeReader code, InputStream subStream)
{
String err = null;
SubStream sub = null;
if(code == null)
throw new NullPointerException("code is NULL");
if(!(subStream instanceof SubStream))
err = "subStream passed was not created using createSubStream call";
else {
sub = (SubStream) subStream;
if(sub.parentThis != this)
err = "subStream was created from another SplitInputStream instance";
else if(sub.readerNo > 0 || sub.readerId > 0)
err = "subStream already in use";
}
if(err != null)
throw new RuntimeException(err);
int minBufSize = sub.minReadSize + sub.readCushion + 512;
if(minBufSize > bufSize)
{
if(q != null)
reallocBuf(minBufSize);
bufSize = minBufSize;
}
int newSubReaderIds[] = new int[subReaderIds.length+1];
IterativeReader newCodes[] = new IterativeReader[codes.length+1];
SubStream newSubStreams[] = new SubStream[subStreams.length+1];
System.arraycopy(subReaderIds, 0, newSubReaderIds, 0, subReaderIds.length);
System.arraycopy(codes, 0, newCodes, 0, codes.length);
System.arraycopy(subStreams, 0, newSubStreams, 0, subStreams.length);
newCodes[codes.length] = code;
newSubStreams[subStreams.length] = sub;
maxReaderId++;
int retVal = maxReaderId;
newSubReaderIds[subReaderIds.length] = retVal;
sub.readerNo = newSubStreams.length;
sub.readerId = retVal;
int newQPtrs[] = new int[qPtrs.length + 1];
System.arraycopy(qPtrs, 0, newQPtrs, 0, qPtrs.length);
int i;
newQPtrs[qPtrs.length] = findQBegin(null);
qPtrs = newQPtrs;
subReaderIds = newSubReaderIds;
codes = newCodes;
subStreams = newSubStreams;
return retVal;
}
/**
* Detaches a Sub Reader and its Stream. Note that it is not necessary to
* call this method explicitly as the Sub Reader is automatically detached
* when the nextRead() returns IterativeReader.STOP or when the subStream is
* closed.
* @param subReaderId Id to detach
* @see #attachSubReader(IterativeReader,InputStream)
*/
public void detachSubReader(int subReaderId)
{
int i;
for(i=0; i < subReaderIds.length; i++)
if(subReaderIds[i] == subReaderId)
break;
if(i >= subReaderIds.length)
throw new RuntimeException("SubReaderId " + subReaderId + " Not found for detaching from SplitInputStream");
detachSubReaderNo(i);
}
private void detachSubReaderNo(int subReaderNo)
{
int i, j, readerNo = subReaderNo+1;
codes[subReaderNo] = null;
SubStream detachedStream = subStreams[subReaderNo];
detachedStream.readerNo = -1;
detachedStream.readerId = 0;
detachedStream.closedFlag = true;
subStreams[subReaderNo] = null;
int newSubReaderIds[] = new int[subReaderIds.length-1];
IterativeReader newCodes[] = new IterativeReader[codes.length-1];
SubStream newSubStreams[] = new SubStream[subStreams.length-1];
System.arraycopy(subReaderIds, 0, newSubReaderIds, 0, subReaderNo);
System.arraycopy(codes, 0, newCodes, 0, subReaderNo);
System.arraycopy(subStreams, 0, newSubStreams, 0, subReaderNo);
System.arraycopy(subReaderIds, subReaderNo+1, newSubReaderIds, subReaderNo, newSubReaderIds.length-subReaderNo);
System.arraycopy(codes, subReaderNo+1, newCodes, subReaderNo, newCodes.length-subReaderNo);
System.arraycopy(subStreams, subReaderNo+1, newSubStreams, subReaderNo, newSubStreams.length-subReaderNo);
// q.detach(in, subReaderNo+1);
int newQPtrs[] = new int[qPtrs.length - 1];
for(i = 0, j = 0; i < qPtrs.length; ++i)
if(i != readerNo)
{
newQPtrs[j] = qPtrs[i];
++j;
}
codes = newCodes;
subStreams = newSubStreams;
qPtrs = newQPtrs;
subReaderIds = newSubReaderIds;
for(i = 0; i < subStreams.length; i++)
if(subStreams[i].readerNo > readerNo)
subStreams[i].readerNo--;
}
private boolean isSkipCall = false;
/**
* Read method of the SplitInputStream.
* See the documentation of InputStream.read(..) for more information. This
* reads from the underlying InputStream and buffers the data for use by the
* subReaders.
* @exception IOException If the underlying stream throws an IOException or
* if the nextRead() call of a subReader throws an IOException
*/
public int read(byte b[], int off, int len) throws IOException
{
boolean isSkip = isSkipCall;
isSkipCall = false;
byte b1, b2;
if(!isSkip)
{
if(len < 0)
throw new IndexOutOfBoundsException("Negative Length Read attempted, len = " + len);
b1 = b[off];
b2 = b[off + len -1];
}
if(len <= 0)
return 0;
if(q == null)
q = new byte[bufSize];
int remain = len;
// Begin q.dequeue(in, 0, b, off, remain)
int qReadSize = qEnd - qPtrs[0];
if(qReadSize > remain)
qReadSize = remain;
if(qReadSize > 0)
{
if(b != null)
System.arraycopy(q, qPtrs[0], b, off, qReadSize);
remain -= qReadSize;
off += qReadSize;
}
// End q.dequeue(in, 0, b, off, remain)
qPtrs[0] += len;
requestBuf = b;
requestPos = off;
requestRemain = remain;
int i, testPtr, indexArr[] = null;
maxBufUsage = 0;
while(requestRemain > 0 && (qPtrs.length > 1 || !eofFlag))
{
if(indexArr == null)
indexArr = new int[1];
int newQBegin = findQBegin(indexArr);
int newQ2nd = 0x7FFFFFFF;
int newQBeginIndex = indexArr[0], newQ2ndIndex = 0;
if(qPtrs.length > 1)
{
i = 0;
do
{
if(i != newQBeginIndex && (testPtr = qPtrs[i])
<= newQ2nd)
{
newQ2nd = testPtr;
newQ2ndIndex = i;
}
++i;
} while(i < qPtrs.length);
}
if(newQBegin >= qEnd)
{
i = 0;
if(eofFlag)
// Wrapup
do {
IterativeReader reader = codes[0];
SubStream stream = subStreams[0];
curReaderId = subReaderIds[0];
reader.nextRead(stream.minReadSize);
curReaderId = -1;
if(!stream.closedFlag)
detachSubReaderNo(0);
} while(codes.length > 0);
else
{
remain = requestRemain;
requestRemain = 0;
// Should be = qEnd.
qPtrs[0] -= remain;
qReadSize = bufSize;
if(qReadSize > remain)
qReadSize = remain;
q2nd = newQBegin;
qReadSize = fillAndDequeue(0, b, requestPos, qReadSize);
requestPos += qReadSize;
requestRemain = remain - qReadSize;
if(!eofFlag)
qPtrs[0] += requestRemain;
}
}
else
{
q2nd = newQ2nd;
int headStreamNo = newQBeginIndex - 1;
IterativeReader headReader = codes[headStreamNo];
SubStream headStream = subStreams[headStreamNo];
int numBytes = qEnd - newQBegin - headStream.readCushion;
SubStream nextStream = null;
if(newQ2ndIndex > 0)
nextStream = subStreams[newQ2ndIndex-1];
if(numBytes < headStream.minReadSize || newQ2ndIndex == 0
|| (qEnd - newQ2nd - nextStream.readCushion)
< nextStream.minReadSize)
numBytes += bufSize - (qEnd - newQ2nd);
curReaderId = subReaderIds[headStreamNo];
int readerStatus = headReader.nextRead(numBytes);
curReaderId = -1;
if(!headStream.closedFlag
&& (readerStatus != IterativeReader.CONTINUE))
detachSubReaderNo(headStreamNo);
}
}
if(q.length > bufSize && maxBufUsage > 0 && q.length - maxBufUsage >= incSize)
{
int resizeLen = bufSize;
if(maxBufUsage > bufSize)
resizeLen = maxBufUsage + (incSize - 1 -
(maxBufUsage - 1 - bufSize)%incSize);
reallocBuf(resizeLen);
}
int retVal = len - requestRemain;
requestBuf = null;
requestRemain = 0;
if(retVal == 0)
retVal = -1;
return retVal;
}
/**
* Read method of the SplitInputStream.
* See the documentation of InputStream.read(..) for more information. This
* reads from the underlying InputStream and buffers the data for use by the
* subReaders.
* @exception IOException If the underlying stream throws an IOException or
* if the nextRead() call of a subReader throws an IOException
*/
public int read() throws IOException
{
int retVal = -1;
if(read(oneByteArr, 0, 1) == 1)
retVal = oneByteArr[0] & 255;
return retVal;
}
/**
* Skip method of the SplitInputStream.
* See the documentation of InputStream.skip(..) for more information. This
* skips from the underlying InputStream and buffers the data for use by the
* subReaders.
* @exception IOException If the underlying stream throws an IOException or
* if the nextRead() call of a subReader throws an IOException
*/
public long skip(long n) throws IOException
{
long retVal = n;
if(retVal < 0)
retVal = 0;
if(retVal > 0)
{
isSkipCall = true;
retVal = read(null, 0, (int)retVal);
if(retVal < 0)
retVal = 0;
}
return retVal;
}
/**
* Available method of the SplitInputStream.
* See the documentation of InputStream.available(..) for more information.
* This method does not strictly conform to InputStream spec since it always
* returns atleast 1 unless the end of file is reached.
*/
public int available()
{
int retVal = qEnd - qPtrs[0];
if(retVal <= 0)
retVal = eofFlag?0:1;
return retVal;
}
/**
* Returns the max bufSize. For debugging and informational purposes.
* @return Max Buf Size
*/
public int getMaxBufSize()
{
return maxBufSize;
}
/**
* This should be called when the Main Object (Say ImageReader) has finished
* reading. This is to ensure that all the SubReaders finish reading any
* data left in the buffer/mainStream and detach.
*/
public void wrapup() throws IOException
{
int readerNo;
while(codes.length > 0)
skip(100000);
q = null;
}
}