224 lines
7.9 KiB
Java
224 lines
7.9 KiB
Java
|
|
package com.common.io;
|
||
|
|
|
||
|
|
import java.io.IOException;
|
||
|
|
import java.io.InputStream;
|
||
|
|
import java.io.ObjectInput;
|
||
|
|
import java.io.OutputStream;
|
||
|
|
|
||
|
|
import com.common.util.IList;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Allows the caller to read the components of the stream they are interested in.
|
||
|
|
* Requests to read missing components will not corrupt the stream.
|
||
|
|
*/
|
||
|
|
public class IndexedObjectInputStream extends AbstractObjectInputStream {
|
||
|
|
/** The stream being wrapped. */
|
||
|
|
private InputStream wrapped = null;
|
||
|
|
/** The buffered content. */
|
||
|
|
private byte[] buffer = null;
|
||
|
|
/** The array of element indices. Each element is an index for an element supplied during serialization. */
|
||
|
|
private int[] indices = null;
|
||
|
|
/** The array of index element offsets within the buffer. The index of the size corresponds to the index in the indices array. */
|
||
|
|
private int[] offsets = null;
|
||
|
|
/** The last used index from the indices array. */
|
||
|
|
private int lastIndicesIndex = 0;
|
||
|
|
/** The index within the indicies array of the index we are about to read, or -1. */
|
||
|
|
private int nextIndex = -1;
|
||
|
|
/** The offset within the buffer of the next data byte to be read, associated with the indexed data element referenced by nextIndex. */
|
||
|
|
private int nextOffset = -1;
|
||
|
|
/** The count of remaining bytes (within the buffer) of the data associated with the indexed data element referenced by nextIndex. */
|
||
|
|
private int nextRemainingSize = -1;
|
||
|
|
/**
|
||
|
|
* IndexedObjectInputStream constructor.
|
||
|
|
* @param in
|
||
|
|
* @throws IOException
|
||
|
|
*/
|
||
|
|
public IndexedObjectInputStream(ObjectInput in) throws IOException {
|
||
|
|
super(in instanceof IObjectInputStream ? ((IObjectInputStream) in).getLoader() : null, in instanceof IObjectInputStream ? ((IObjectInputStream) in).getClassReplacementHandler() : null, in instanceof IObjectInputStream ? ((IObjectInputStream) in).getClassList() : null, in instanceof IObjectInputStream ? ((IObjectInputStream) in).getInstanceFactory() : null, in instanceof IObjectInputStream ? ((IObjectInputStream) in).getClassTracker() : null, in instanceof IObjectInputStream ? ((IObjectInputStream) in).getDeserializedValues() : null, in instanceof IObjectInputStream ? ((IObjectInputStream) in).getDefaultNumberStyle() : AbstractStandardInputStream.STYLE_DEFAULT);
|
||
|
|
this.wrapped = (InputStream) in;
|
||
|
|
initialize();
|
||
|
|
}//IndexedObjectInputStream()//
|
||
|
|
/**
|
||
|
|
* IndexedObjectInputStream constructor.
|
||
|
|
* @param in
|
||
|
|
* @throws IOException
|
||
|
|
*/
|
||
|
|
public IndexedObjectInputStream(IObjectInputStream in) throws IOException {
|
||
|
|
super(in.getLoader(), in.getClassReplacementHandler(), in.getClassList(), in.getInstanceFactory(), in.getClassTracker(), in.getDeserializedValues(), in.getDefaultNumberStyle());
|
||
|
|
this.wrapped = (InputStream) in;
|
||
|
|
initialize();
|
||
|
|
}//IndexedObjectInputStream()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#skip(long)
|
||
|
|
*/
|
||
|
|
public long skip(long count) throws IOException {
|
||
|
|
//Not supported.//
|
||
|
|
return 0;
|
||
|
|
}//skip()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#mark(int)
|
||
|
|
*/
|
||
|
|
public void mark(int readLimit) {
|
||
|
|
//Not supported.//
|
||
|
|
}//mark()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#markSupported()
|
||
|
|
*/
|
||
|
|
public boolean markSupported() {
|
||
|
|
//Not supported.//
|
||
|
|
return false;
|
||
|
|
}//markSupported()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#available()
|
||
|
|
*/
|
||
|
|
public int available() throws IOException {
|
||
|
|
return wrapped.available();
|
||
|
|
}//available()//
|
||
|
|
/**
|
||
|
|
* Initializes the stream.
|
||
|
|
* @throws IOException
|
||
|
|
*/
|
||
|
|
protected void initialize() throws IOException {
|
||
|
|
int count = StreamSupport.readInt(wrapped, StreamSupport.NUMBER_VLSF);
|
||
|
|
int lastSize = -1;
|
||
|
|
int totalSize = 0;
|
||
|
|
|
||
|
|
indices = new int[count];
|
||
|
|
offsets = new int[count];
|
||
|
|
|
||
|
|
for(int i = 0; i < count; i++) {
|
||
|
|
//Set the offset within the buffer.//
|
||
|
|
offsets[i] = lastSize != -1 ? lastSize + offsets[i - 1] : 0;
|
||
|
|
//Read the identifier for the value.//
|
||
|
|
indices[i] = StreamSupport.readInt(wrapped, StreamSupport.NUMBER_VLSF);
|
||
|
|
//Read the offset of the start of the value.//
|
||
|
|
lastSize = StreamSupport.readInt(wrapped, StreamSupport.NUMBER_VLSF);
|
||
|
|
//Update the total size of the data byte array.//
|
||
|
|
totalSize += lastSize;
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Setup the content buffer.//
|
||
|
|
buffer = new byte[totalSize];
|
||
|
|
|
||
|
|
//Buffer the whole content.//
|
||
|
|
if(StreamSupport.readBytes(wrapped, buffer) != buffer.length) {
|
||
|
|
throw new IOException("Failed to read enough bytes.");
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
lastIndicesIndex = offsets.length - 1;
|
||
|
|
}//initialize()//
|
||
|
|
/**
|
||
|
|
* Begins the deserialization of the value at the given index.
|
||
|
|
* <p>This must be called prior to any other read operation, for every read operation performed.</p>
|
||
|
|
* @param index The index of the value to be deserialized.
|
||
|
|
* @return Whether the stream contains a value associated with the index.
|
||
|
|
*/
|
||
|
|
public boolean begin(int index) {
|
||
|
|
int i = lastIndicesIndex;
|
||
|
|
|
||
|
|
nextIndex = -1;
|
||
|
|
|
||
|
|
//Use a rotating loop over the indices starting with the index of the last match plus one. Often things are read in the same order they were written so this is just more efficient.//
|
||
|
|
do {
|
||
|
|
i++;
|
||
|
|
|
||
|
|
if(i == indices.length) {
|
||
|
|
i = 0;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(indices[i] == index) {
|
||
|
|
nextIndex = i;
|
||
|
|
nextOffset = offsets[i];
|
||
|
|
nextRemainingSize = offsets.length > i + 1 ? offsets[i + 1] - offsets[i] : buffer.length - offsets[i];
|
||
|
|
}//if//
|
||
|
|
} while(nextIndex == -1 && i != lastIndicesIndex);
|
||
|
|
|
||
|
|
lastIndicesIndex = i;
|
||
|
|
|
||
|
|
return nextIndex != -1;
|
||
|
|
}//begin()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#read()
|
||
|
|
*/
|
||
|
|
public int read() throws IOException {
|
||
|
|
if(nextIndex == -1) {
|
||
|
|
throw new IOException("Must call begin(int) prior to reading data. Each piece of data must be indexed.");
|
||
|
|
}//if//
|
||
|
|
else if(nextRemainingSize <= 0) {
|
||
|
|
throw new IOException("End of element stream reached.");
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
return buffer[nextOffset++];
|
||
|
|
}//read()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#read(byte[])
|
||
|
|
*/
|
||
|
|
public int read(byte[] buffer) throws IOException {
|
||
|
|
int result = Math.min(buffer.length, nextRemainingSize);
|
||
|
|
|
||
|
|
if(nextIndex == -1) {
|
||
|
|
throw new IOException("Must call begin(int) prior to reading data. Each piece of data must be indexed.");
|
||
|
|
}//if//
|
||
|
|
else if(nextRemainingSize <= 0) {
|
||
|
|
throw new IOException("End of element stream reached.");
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
System.arraycopy(this.buffer, nextOffset, buffer, 0, result);
|
||
|
|
nextOffset += result;
|
||
|
|
nextRemainingSize -= result;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//read()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#read(byte[], int, int)
|
||
|
|
*/
|
||
|
|
public int read(byte[] buffer, int offset, int length) throws IOException {
|
||
|
|
int result = Math.min(length, nextRemainingSize);
|
||
|
|
|
||
|
|
if(nextIndex == -1) {
|
||
|
|
throw new IOException("Must call begin(int) prior to reading data. Each piece of data must be indexed.");
|
||
|
|
}//if//
|
||
|
|
else if(nextRemainingSize <= 0) {
|
||
|
|
throw new IOException("End of element stream reached.");
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
System.arraycopy(this.buffer, nextOffset, buffer, offset, result);
|
||
|
|
nextOffset += result;
|
||
|
|
nextRemainingSize -= result;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//read()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractStandardInputStream#read(java.io.OutputStream, int)
|
||
|
|
*/
|
||
|
|
public int read(OutputStream out, int length) throws IOException {
|
||
|
|
byte[] bytes = new byte[Math.min(length, nextRemainingSize)];
|
||
|
|
|
||
|
|
if(nextIndex == -1) {
|
||
|
|
throw new IOException("Must call begin(int) prior to reading data. Each piece of data must be indexed.");
|
||
|
|
}//if//
|
||
|
|
else if(nextRemainingSize <= 0) {
|
||
|
|
throw new IOException("End of element stream reached.");
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
System.arraycopy(this.buffer, nextOffset, bytes, 0, bytes.length);
|
||
|
|
nextOffset += bytes.length;
|
||
|
|
nextRemainingSize -= bytes.length;
|
||
|
|
out.write(bytes);
|
||
|
|
|
||
|
|
return bytes.length;
|
||
|
|
}//read()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.common.io.AbstractObjectInputStream#peekByte(int)
|
||
|
|
*/
|
||
|
|
public byte peekByte(int offset) throws IOException {
|
||
|
|
if(nextIndex == -1) {
|
||
|
|
throw new IOException("Must call begin(int) prior to reading data. Each piece of data must be indexed.");
|
||
|
|
}//if//
|
||
|
|
else if(nextRemainingSize <= 0) {
|
||
|
|
throw new IOException("End of element stream reached.");
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
return buffer[nextOffset];
|
||
|
|
}//peekByte()//
|
||
|
|
}//IndexedObjectInputStream//
|