|
|
|
|
@@ -25,14 +25,14 @@ import java.util.LinkedList;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A speedy alternative to {@link java.io.ByteArrayOutputStream}.
|
|
|
|
|
* <p>Unlike {@link java.io.ByteArrayOutputStream}, this implementation is backed by a
|
|
|
|
|
* {@link java.util.LinkedList} of byte[] instead of 1 constantly resizing byte[].
|
|
|
|
|
* It does not copy buffers when it's expanded.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>Unlike {@link java.io.ByteArrayOutputStream}, this implementation is backed
|
|
|
|
|
* by a {@link java.util.LinkedList} of {@code byte[]} instead of 1 constantly
|
|
|
|
|
* resizing {@code byte[]}. It does not copy buffers when it gets expanded.
|
|
|
|
|
*
|
|
|
|
|
* <p>The initial buffer is only created when the stream is first written.
|
|
|
|
|
* There's also no copying of the internal buffer if its contents is extracted with the
|
|
|
|
|
* {@link #writeTo(OutputStream)} method.
|
|
|
|
|
* Instances of this class are NOT THREAD SAFE.</p>
|
|
|
|
|
* There is also no copying of the internal buffer if its contents is extracted
|
|
|
|
|
* with the {@link #writeTo(OutputStream)} method.
|
|
|
|
|
*
|
|
|
|
|
* @author Craig Andrews
|
|
|
|
|
* @since 4.2
|
|
|
|
|
@@ -41,28 +41,30 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
|
|
|
|
|
private static final int DEFAULT_BLOCK_SIZE = 256;
|
|
|
|
|
|
|
|
|
|
// the buffers used to store the content bytes
|
|
|
|
|
|
|
|
|
|
// The buffers used to store the content bytes
|
|
|
|
|
private final LinkedList<byte[]> buffers = new LinkedList<byte[]>();
|
|
|
|
|
|
|
|
|
|
// is the stream closed?
|
|
|
|
|
private boolean closed = false;
|
|
|
|
|
// The size, in bytes, to use when allocating the first byte[]
|
|
|
|
|
private final int initialBlockSize;
|
|
|
|
|
|
|
|
|
|
// the size, in bytes, to use when allocating the next next byte[]
|
|
|
|
|
private int nextBlockSize;
|
|
|
|
|
// The size, in bytes, to use when allocating the next next byte[]
|
|
|
|
|
private int nextBlockSize = 0;
|
|
|
|
|
|
|
|
|
|
// the index in the byte[] found at buffers.getLast() to be written next
|
|
|
|
|
private int index = 0;
|
|
|
|
|
|
|
|
|
|
// number of bytes in previous buffers
|
|
|
|
|
// the number of bytes in the current buffer is in index
|
|
|
|
|
// The number of bytes in previous buffers.
|
|
|
|
|
// (The number of bytes in the current buffer is in 'index'.)
|
|
|
|
|
private int alreadyBufferedSize = 0;
|
|
|
|
|
|
|
|
|
|
// the size, in bytes, to use when allocating the first byte[]
|
|
|
|
|
private final int initialBlockSize;
|
|
|
|
|
// The index in the byte[] found at buffers.getLast() to be written next
|
|
|
|
|
private int index = 0;
|
|
|
|
|
|
|
|
|
|
// Is the stream closed?
|
|
|
|
|
private boolean closed = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a new <code>FastByteArrayOutputStream</code>
|
|
|
|
|
* with the default initial capacity of {@value #DEFAULT_BLOCK_SIZE} bytes.
|
|
|
|
|
* with the default initial capacity of 256 bytes.
|
|
|
|
|
*/
|
|
|
|
|
public FastByteArrayOutputStream() {
|
|
|
|
|
this(DEFAULT_BLOCK_SIZE);
|
|
|
|
|
@@ -76,20 +78,23 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
public FastByteArrayOutputStream(int initialBlockSize) {
|
|
|
|
|
Assert.isTrue(initialBlockSize > 0, "Initial block size must be greater than 0");
|
|
|
|
|
this.initialBlockSize = initialBlockSize;
|
|
|
|
|
nextBlockSize = initialBlockSize;
|
|
|
|
|
this.nextBlockSize = initialBlockSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Overridden methods
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void write(int datum) throws IOException {
|
|
|
|
|
if (closed) {
|
|
|
|
|
if (this.closed) {
|
|
|
|
|
throw new IOException("Stream closed");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (buffers.peekLast() == null || buffers.getLast().length == index) {
|
|
|
|
|
if (this.buffers.peekLast() == null || this.buffers.getLast().length == this.index) {
|
|
|
|
|
addBuffer(1);
|
|
|
|
|
}
|
|
|
|
|
// store the byte
|
|
|
|
|
buffers.getLast()[index++] = (byte) datum;
|
|
|
|
|
this.buffers.getLast()[this.index++] = (byte) datum;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -98,60 +103,81 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
if (data == null) {
|
|
|
|
|
throw new NullPointerException();
|
|
|
|
|
}
|
|
|
|
|
else if ((offset < 0) || ((offset + length) > data.length) || (length < 0)) {
|
|
|
|
|
else if (offset < 0 || offset + length > data.length || length < 0) {
|
|
|
|
|
throw new IndexOutOfBoundsException();
|
|
|
|
|
}
|
|
|
|
|
else if (closed) {
|
|
|
|
|
else if (this.closed) {
|
|
|
|
|
throw new IOException("Stream closed");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (buffers.peekLast() == null || buffers.getLast().length == index) {
|
|
|
|
|
if (this.buffers.peekLast() == null || this.buffers.getLast().length == this.index) {
|
|
|
|
|
addBuffer(length);
|
|
|
|
|
}
|
|
|
|
|
if ((index + length) > buffers.getLast().length) {
|
|
|
|
|
if (this.index + length > this.buffers.getLast().length) {
|
|
|
|
|
int pos = offset;
|
|
|
|
|
do {
|
|
|
|
|
if (index == buffers.getLast().length) {
|
|
|
|
|
if (this.index == this.buffers.getLast().length) {
|
|
|
|
|
addBuffer(length);
|
|
|
|
|
}
|
|
|
|
|
int copyLength = buffers.getLast().length - index;
|
|
|
|
|
int copyLength = this.buffers.getLast().length - this.index;
|
|
|
|
|
if (length < copyLength) {
|
|
|
|
|
copyLength = length;
|
|
|
|
|
}
|
|
|
|
|
System.arraycopy(data, offset, buffers.getLast(), index, copyLength);
|
|
|
|
|
offset += copyLength;
|
|
|
|
|
index += copyLength;
|
|
|
|
|
System.arraycopy(data, pos, this.buffers.getLast(), this.index, copyLength);
|
|
|
|
|
pos += copyLength;
|
|
|
|
|
this.index += copyLength;
|
|
|
|
|
length -= copyLength;
|
|
|
|
|
} while (length > 0);
|
|
|
|
|
}
|
|
|
|
|
while (length > 0);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Copy in the subarray
|
|
|
|
|
System.arraycopy(data, offset, buffers.getLast(), index, length);
|
|
|
|
|
index += length;
|
|
|
|
|
// copy in the sub-array
|
|
|
|
|
System.arraycopy(data, offset, this.buffers.getLast(), this.index, length);
|
|
|
|
|
this.index += length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void close() {
|
|
|
|
|
closed = true;
|
|
|
|
|
this.closed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the number of bytes stored in this <code>FastByteArrayOutputStream</code>
|
|
|
|
|
* Convert the buffer's contents into a string decoding bytes using the
|
|
|
|
|
* platform's default character set. The length of the new <tt>String</tt>
|
|
|
|
|
* is a function of the character set, and hence may not be equal to the
|
|
|
|
|
* size of the buffer.
|
|
|
|
|
* <p>This method always replaces malformed-input and unmappable-character
|
|
|
|
|
* sequences with the default replacement string for the platform's
|
|
|
|
|
* default character set. The {@linkplain java.nio.charset.CharsetDecoder}
|
|
|
|
|
* class should be used when more control over the decoding process is
|
|
|
|
|
* required.
|
|
|
|
|
* @return a String decoded from the buffer's contents
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
return new String(toByteArrayUnsafe());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Custom methods
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the number of bytes stored in this <code>FastByteArrayOutputStream</code>.
|
|
|
|
|
*/
|
|
|
|
|
public int size() {
|
|
|
|
|
return alreadyBufferedSize + index;
|
|
|
|
|
return (this.alreadyBufferedSize + this.index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert the stream's data to a byte array and return the byte array.
|
|
|
|
|
*
|
|
|
|
|
* <p>Also replaces the internal structures with the byte array to conserve memory:
|
|
|
|
|
* if the byte array is being made anyways, mind as well as use it.
|
|
|
|
|
* This approach also means that if this method is called twice without any writes in between,
|
|
|
|
|
* if the byte array is being made anyways, mind as well as use it. This approach
|
|
|
|
|
* also means that if this method is called twice without any writes in between,
|
|
|
|
|
* the second call is a no-op.
|
|
|
|
|
* This method is "unsafe" as it returns the internal buffer - callers should not modify the returned buffer.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>This method is "unsafe" as it returns the internal buffer.
|
|
|
|
|
* Callers should not modify the returned buffer.
|
|
|
|
|
* @return the current contents of this output stream, as a byte array.
|
|
|
|
|
* @see #size()
|
|
|
|
|
* @see #toByteArray()
|
|
|
|
|
@@ -162,16 +188,14 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
return new byte[0];
|
|
|
|
|
}
|
|
|
|
|
resize(totalSize);
|
|
|
|
|
return buffers.getFirst();
|
|
|
|
|
return this.buffers.getFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a newly allocated byte array.
|
|
|
|
|
*
|
|
|
|
|
* <p>Its size is the current
|
|
|
|
|
* size of this output stream and the valid contents of the buffer
|
|
|
|
|
* have been copied into it.</p>
|
|
|
|
|
*
|
|
|
|
|
* @return the current contents of this output stream, as a byte array.
|
|
|
|
|
* @see #size()
|
|
|
|
|
* @see #toByteArrayUnsafe()
|
|
|
|
|
@@ -184,54 +208,49 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resets the contents of this <code>FastByteArrayOutputStreamInputStream</code>
|
|
|
|
|
* Reset the contents of this <code>FastByteArrayOutputStream</code>.
|
|
|
|
|
* <p>All currently accumulated output in the output stream is discarded.
|
|
|
|
|
* The output stream can be used again.</p>
|
|
|
|
|
* The output stream can be used again.
|
|
|
|
|
*/
|
|
|
|
|
public void reset() {
|
|
|
|
|
buffers.clear();
|
|
|
|
|
nextBlockSize = initialBlockSize;
|
|
|
|
|
closed = false;
|
|
|
|
|
index = 0;
|
|
|
|
|
alreadyBufferedSize = 0;
|
|
|
|
|
this.buffers.clear();
|
|
|
|
|
this.nextBlockSize = this.initialBlockSize;
|
|
|
|
|
this.closed = false;
|
|
|
|
|
this.index = 0;
|
|
|
|
|
this.alreadyBufferedSize = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get an {@link java.io.InputStream} to retrieve the data in this OutputStream
|
|
|
|
|
*
|
|
|
|
|
* Get an {@link InputStream} to retrieve the data in this OutputStream.
|
|
|
|
|
* <p>Note that if any methods are called on the OutputStream
|
|
|
|
|
* (including, but not limited to, any of the write methods, {@link #reset()},
|
|
|
|
|
* {@link #toByteArray()}, and {@link #toByteArrayUnsafe()}) then the {@link java.io.InputStream}'s
|
|
|
|
|
* behavior is undefined.</p>
|
|
|
|
|
*
|
|
|
|
|
* @return {@link java.io.InputStream} of the contents of this <code>FastByteArrayOutputStreamInputStream</code>
|
|
|
|
|
* {@link #toByteArray()}, and {@link #toByteArrayUnsafe()}) then the
|
|
|
|
|
* {@link java.io.InputStream}'s behavior is undefined.
|
|
|
|
|
* @return {@link InputStream} of the contents of this OutputStream
|
|
|
|
|
*/
|
|
|
|
|
public InputStream getInputStream() {
|
|
|
|
|
return new FastByteArrayOutputStreamInputStream(this);
|
|
|
|
|
return new FastByteArrayInputStream(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Write the buffers content to the given OutputStream
|
|
|
|
|
*
|
|
|
|
|
* Write the buffers content to the given OutputStream.
|
|
|
|
|
* @param out the OutputStream to write to
|
|
|
|
|
*/
|
|
|
|
|
public void writeTo(OutputStream out) throws IOException {
|
|
|
|
|
Iterator<byte[]> iter = buffers.iterator();
|
|
|
|
|
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
byte[] bytes = iter.next();
|
|
|
|
|
if (iter.hasNext()) {
|
|
|
|
|
Iterator<byte[]> it = this.buffers.iterator();
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
byte[] bytes = it.next();
|
|
|
|
|
if (it.hasNext()) {
|
|
|
|
|
out.write(bytes, 0, bytes.length);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
out.write(bytes, 0, index);
|
|
|
|
|
out.write(bytes, 0, this.index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resize the internal buffer size to a specified capacity.
|
|
|
|
|
*
|
|
|
|
|
* @param targetCapacity the desired size of the buffer
|
|
|
|
|
* @throws IllegalArgumentException if the given capacity is smaller than
|
|
|
|
|
* the actual size of the content stored in the buffer already
|
|
|
|
|
@@ -240,54 +259,53 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
public void resize(int targetCapacity) {
|
|
|
|
|
Assert.isTrue(targetCapacity >= size(), "New capacity must not be smaller than current size");
|
|
|
|
|
if (buffers.peekFirst() == null) {
|
|
|
|
|
nextBlockSize = targetCapacity - size();
|
|
|
|
|
this.nextBlockSize = targetCapacity - size();
|
|
|
|
|
}
|
|
|
|
|
else if (size() == targetCapacity && buffers.getFirst().length == targetCapacity) {
|
|
|
|
|
else if (size() == targetCapacity && this.buffers.getFirst().length == targetCapacity) {
|
|
|
|
|
// do nothing - already at the targetCapacity
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
int totalSize = size();
|
|
|
|
|
byte[] data = new byte[targetCapacity];
|
|
|
|
|
int pos = 0;
|
|
|
|
|
Iterator<byte[]> iter = buffers.iterator();
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
byte[] bytes = iter.next();
|
|
|
|
|
if (iter.hasNext()) {
|
|
|
|
|
Iterator<byte[]> it = this.buffers.iterator();
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
byte[] bytes = it.next();
|
|
|
|
|
if (it.hasNext()) {
|
|
|
|
|
System.arraycopy(bytes, 0, data, pos, bytes.length);
|
|
|
|
|
pos += bytes.length;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
System.arraycopy(bytes, 0, data, pos, index);
|
|
|
|
|
System.arraycopy(bytes, 0, data, pos, this.index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
buffers.clear();
|
|
|
|
|
buffers.add(data);
|
|
|
|
|
index = totalSize;
|
|
|
|
|
alreadyBufferedSize = 0;
|
|
|
|
|
this.buffers.clear();
|
|
|
|
|
this.buffers.add(data);
|
|
|
|
|
this.index = totalSize;
|
|
|
|
|
this.alreadyBufferedSize = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a new buffer and store it in the LinkedList
|
|
|
|
|
*
|
|
|
|
|
* <p>Adds a new buffer that can store at least {@code minCapacity} bytes</p>
|
|
|
|
|
* <p>Adds a new buffer that can store at least {@code minCapacity} bytes.
|
|
|
|
|
*/
|
|
|
|
|
private void addBuffer(int minCapacity) {
|
|
|
|
|
if (buffers.peekLast() != null) {
|
|
|
|
|
alreadyBufferedSize += index;
|
|
|
|
|
index = 0;
|
|
|
|
|
if (this.buffers.peekLast() != null) {
|
|
|
|
|
this.alreadyBufferedSize += this.index;
|
|
|
|
|
this.index = 0;
|
|
|
|
|
}
|
|
|
|
|
if (nextBlockSize < minCapacity) {
|
|
|
|
|
nextBlockSize = nextPowerOf2(minCapacity);
|
|
|
|
|
if (this.nextBlockSize < minCapacity) {
|
|
|
|
|
this.nextBlockSize = nextPowerOf2(minCapacity);
|
|
|
|
|
}
|
|
|
|
|
buffers.add(new byte[nextBlockSize]);
|
|
|
|
|
nextBlockSize *= 2; // block size doubles each time
|
|
|
|
|
this.buffers.add(new byte[this.nextBlockSize]);
|
|
|
|
|
this.nextBlockSize *= 2; // block size doubles each time
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the next power of 2 of a number (ex, the next power of 2 of 119 is 128)
|
|
|
|
|
* Get the next power of 2 of a number (ex, the next power of 2 of 119 is 128).
|
|
|
|
|
*/
|
|
|
|
|
private static final int nextPowerOf2(int val) {
|
|
|
|
|
private static int nextPowerOf2(int val) {
|
|
|
|
|
val--;
|
|
|
|
|
val = (val >> 1) | val;
|
|
|
|
|
val = (val >> 2) | val;
|
|
|
|
|
@@ -298,88 +316,67 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Converts the buffer's contents into a string decoding bytes using the
|
|
|
|
|
* platform's default character set. The length of the new <tt>String</tt>
|
|
|
|
|
* is a function of the character set, and hence may not be equal to the
|
|
|
|
|
* size of the buffer.
|
|
|
|
|
*
|
|
|
|
|
* <p>This method always replaces malformed-input and unmappable-character
|
|
|
|
|
* sequences with the default replacement string for the platform's
|
|
|
|
|
* default character set. The {@linkplain java.nio.charset.CharsetDecoder}
|
|
|
|
|
* class should be used when more control over the decoding process is
|
|
|
|
|
* required.</p>
|
|
|
|
|
*
|
|
|
|
|
* @return String decoded from the buffer's contents.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
return new String(toByteArrayUnsafe());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An implementation of {@link java.io.InputStream} that reads from <code>FastByteArrayOutputStream</code>
|
|
|
|
|
* Instances of this class are NOT THREAD SAFE.
|
|
|
|
|
* An implementation of {@link java.io.InputStream} that reads from a given
|
|
|
|
|
* <code>FastByteArrayOutputStream</code>.
|
|
|
|
|
*/
|
|
|
|
|
private static final class FastByteArrayOutputStreamInputStream extends UpdateMessageDigestInputStream {
|
|
|
|
|
int totalBytesRead = 0;
|
|
|
|
|
private static final class FastByteArrayInputStream extends UpdateMessageDigestInputStream {
|
|
|
|
|
|
|
|
|
|
int nextIndexInCurrentBuffer = 0;
|
|
|
|
|
private final FastByteArrayOutputStream fastByteArrayOutputStream;
|
|
|
|
|
|
|
|
|
|
final Iterator<byte[]> buffersIterator;
|
|
|
|
|
private final Iterator<byte[]> buffersIterator;
|
|
|
|
|
|
|
|
|
|
byte[] currentBuffer;
|
|
|
|
|
private byte[] currentBuffer;
|
|
|
|
|
|
|
|
|
|
int currentBufferLength;
|
|
|
|
|
private int currentBufferLength = 0;
|
|
|
|
|
|
|
|
|
|
final FastByteArrayOutputStream fastByteArrayOutputStream;
|
|
|
|
|
private int nextIndexInCurrentBuffer = 0;
|
|
|
|
|
|
|
|
|
|
private int totalBytesRead = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a new <code>FastByteArrayOutputStreamInputStream</code> backed
|
|
|
|
|
* by the given <code>FastByteArrayOutputStream</code>
|
|
|
|
|
* by the given <code>FastByteArrayOutputStream</code>.
|
|
|
|
|
*/
|
|
|
|
|
public FastByteArrayOutputStreamInputStream(FastByteArrayOutputStream fastByteArrayOutputStream) {
|
|
|
|
|
public FastByteArrayInputStream(FastByteArrayOutputStream fastByteArrayOutputStream) {
|
|
|
|
|
this.fastByteArrayOutputStream = fastByteArrayOutputStream;
|
|
|
|
|
buffersIterator = fastByteArrayOutputStream.buffers.iterator();
|
|
|
|
|
if (buffersIterator.hasNext()) {
|
|
|
|
|
currentBuffer = buffersIterator.next();
|
|
|
|
|
if (currentBuffer == fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
currentBufferLength = fastByteArrayOutputStream.index;
|
|
|
|
|
this.buffersIterator = fastByteArrayOutputStream.buffers.iterator();
|
|
|
|
|
if (this.buffersIterator.hasNext()) {
|
|
|
|
|
this.currentBuffer = this.buffersIterator.next();
|
|
|
|
|
if (this.currentBuffer == fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
this.currentBufferLength = fastByteArrayOutputStream.index;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBufferLength = currentBuffer.length;
|
|
|
|
|
this.currentBufferLength = this.currentBuffer.length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBuffer = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int read() {
|
|
|
|
|
if (currentBuffer == null) {
|
|
|
|
|
if (this.currentBuffer == null) {
|
|
|
|
|
// this stream doesn't have any data in it
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (nextIndexInCurrentBuffer < currentBufferLength) {
|
|
|
|
|
totalBytesRead++;
|
|
|
|
|
return currentBuffer[nextIndexInCurrentBuffer++];
|
|
|
|
|
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
|
|
|
|
|
this.totalBytesRead++;
|
|
|
|
|
return this.currentBuffer[this.nextIndexInCurrentBuffer++];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (buffersIterator.hasNext()) {
|
|
|
|
|
currentBuffer = buffersIterator.next();
|
|
|
|
|
if (currentBuffer == fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
currentBufferLength = fastByteArrayOutputStream.index;
|
|
|
|
|
if (this.buffersIterator.hasNext()) {
|
|
|
|
|
this.currentBuffer = this.buffersIterator.next();
|
|
|
|
|
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
this.currentBufferLength = this.fastByteArrayOutputStream.index;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBufferLength = currentBuffer.length;
|
|
|
|
|
this.currentBufferLength = this.currentBuffer.length;
|
|
|
|
|
}
|
|
|
|
|
nextIndexInCurrentBuffer = 0;
|
|
|
|
|
this.nextIndexInCurrentBuffer = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBuffer = null;
|
|
|
|
|
this.currentBuffer = null;
|
|
|
|
|
}
|
|
|
|
|
return read();
|
|
|
|
|
}
|
|
|
|
|
@@ -409,31 +406,31 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
throw new IllegalArgumentException("off must be 0 or greater: " + off);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (currentBuffer == null) {
|
|
|
|
|
if (this.currentBuffer == null) {
|
|
|
|
|
// this stream doesn't have any data in it
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (nextIndexInCurrentBuffer < currentBufferLength) {
|
|
|
|
|
int bytesToCopy = Math.min(len, currentBufferLength - nextIndexInCurrentBuffer);
|
|
|
|
|
System.arraycopy(currentBuffer, nextIndexInCurrentBuffer, b, off, bytesToCopy);
|
|
|
|
|
totalBytesRead += bytesToCopy;
|
|
|
|
|
nextIndexInCurrentBuffer += bytesToCopy;
|
|
|
|
|
return bytesToCopy + read(b, off + bytesToCopy, len - bytesToCopy);
|
|
|
|
|
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
|
|
|
|
|
int bytesToCopy = Math.min(len, this.currentBufferLength - this.nextIndexInCurrentBuffer);
|
|
|
|
|
System.arraycopy(this.currentBuffer, this.nextIndexInCurrentBuffer, b, off, bytesToCopy);
|
|
|
|
|
this.totalBytesRead += bytesToCopy;
|
|
|
|
|
this.nextIndexInCurrentBuffer += bytesToCopy;
|
|
|
|
|
return (bytesToCopy + read(b, off + bytesToCopy, len - bytesToCopy));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (buffersIterator.hasNext()) {
|
|
|
|
|
currentBuffer = buffersIterator.next();
|
|
|
|
|
if (currentBuffer == fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
currentBufferLength = fastByteArrayOutputStream.index;
|
|
|
|
|
if (this.buffersIterator.hasNext()) {
|
|
|
|
|
this.currentBuffer = this.buffersIterator.next();
|
|
|
|
|
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
this.currentBufferLength = this.fastByteArrayOutputStream.index;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBufferLength = currentBuffer.length;
|
|
|
|
|
this.currentBufferLength = this.currentBuffer.length;
|
|
|
|
|
}
|
|
|
|
|
nextIndexInCurrentBuffer = 0;
|
|
|
|
|
this.nextIndexInCurrentBuffer = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBuffer = null;
|
|
|
|
|
this.currentBuffer = null;
|
|
|
|
|
}
|
|
|
|
|
return read(b, off, len);
|
|
|
|
|
}
|
|
|
|
|
@@ -444,8 +441,7 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
@Override
|
|
|
|
|
public long skip(long n) throws IOException {
|
|
|
|
|
if (n > Integer.MAX_VALUE) {
|
|
|
|
|
throw new IllegalArgumentException("n exceeds maximum (" +
|
|
|
|
|
Integer.MAX_VALUE + "): " + n);
|
|
|
|
|
throw new IllegalArgumentException("n exceeds maximum (" + Integer.MAX_VALUE + "): " + n);
|
|
|
|
|
}
|
|
|
|
|
else if (n == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -454,30 +450,30 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
throw new IllegalArgumentException("n must be 0 or greater: " + n);
|
|
|
|
|
}
|
|
|
|
|
int len = (int) n;
|
|
|
|
|
if (currentBuffer == null) {
|
|
|
|
|
if (this.currentBuffer == null) {
|
|
|
|
|
// this stream doesn't have any data in it
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (nextIndexInCurrentBuffer < currentBufferLength) {
|
|
|
|
|
int bytesToSkip = Math.min(len, currentBufferLength - nextIndexInCurrentBuffer);
|
|
|
|
|
totalBytesRead += bytesToSkip;
|
|
|
|
|
nextIndexInCurrentBuffer += bytesToSkip;
|
|
|
|
|
return bytesToSkip + skip(len - bytesToSkip);
|
|
|
|
|
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
|
|
|
|
|
int bytesToSkip = Math.min(len, this.currentBufferLength - this.nextIndexInCurrentBuffer);
|
|
|
|
|
this.totalBytesRead += bytesToSkip;
|
|
|
|
|
this.nextIndexInCurrentBuffer += bytesToSkip;
|
|
|
|
|
return (bytesToSkip + skip(len - bytesToSkip));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (buffersIterator.hasNext()) {
|
|
|
|
|
currentBuffer = buffersIterator.next();
|
|
|
|
|
if (currentBuffer == fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
currentBufferLength = fastByteArrayOutputStream.index;
|
|
|
|
|
if (this.buffersIterator.hasNext()) {
|
|
|
|
|
this.currentBuffer = this.buffersIterator.next();
|
|
|
|
|
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
this.currentBufferLength = this.fastByteArrayOutputStream.index;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBufferLength = currentBuffer.length;
|
|
|
|
|
this.currentBufferLength = this.currentBuffer.length;
|
|
|
|
|
}
|
|
|
|
|
nextIndexInCurrentBuffer = 0;
|
|
|
|
|
this.nextIndexInCurrentBuffer = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBuffer = null;
|
|
|
|
|
this.currentBuffer = null;
|
|
|
|
|
}
|
|
|
|
|
return skip(len);
|
|
|
|
|
}
|
|
|
|
|
@@ -486,12 +482,11 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int available() {
|
|
|
|
|
return fastByteArrayOutputStream.size() - totalBytesRead;
|
|
|
|
|
return (this.fastByteArrayOutputStream.size() - this.totalBytesRead);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update the message digest with the remaining bytes in this stream.
|
|
|
|
|
*
|
|
|
|
|
* @param messageDigest The message digest to update
|
|
|
|
|
*/
|
|
|
|
|
public void updateMessageDigest(MessageDigest messageDigest) {
|
|
|
|
|
@@ -505,7 +500,7 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
* @param len how many bytes to read from this stream and use to update the message digest
|
|
|
|
|
*/
|
|
|
|
|
public void updateMessageDigest(MessageDigest messageDigest, int len) {
|
|
|
|
|
if (currentBuffer == null) {
|
|
|
|
|
if (this.currentBuffer == null) {
|
|
|
|
|
// this stream doesn't have any data in it
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@@ -516,29 +511,30 @@ public final class FastByteArrayOutputStream extends OutputStream {
|
|
|
|
|
throw new IllegalArgumentException("len must be 0 or greater: " + len);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (nextIndexInCurrentBuffer < currentBufferLength) {
|
|
|
|
|
int bytesToCopy = Math.min(len, currentBufferLength - nextIndexInCurrentBuffer);
|
|
|
|
|
messageDigest.update(currentBuffer, nextIndexInCurrentBuffer, bytesToCopy);
|
|
|
|
|
nextIndexInCurrentBuffer += bytesToCopy;
|
|
|
|
|
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
|
|
|
|
|
int bytesToCopy = Math.min(len, this.currentBufferLength - this.nextIndexInCurrentBuffer);
|
|
|
|
|
messageDigest.update(this.currentBuffer, this.nextIndexInCurrentBuffer, bytesToCopy);
|
|
|
|
|
this.nextIndexInCurrentBuffer += bytesToCopy;
|
|
|
|
|
updateMessageDigest(messageDigest, len - bytesToCopy);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (buffersIterator.hasNext()) {
|
|
|
|
|
currentBuffer = buffersIterator.next();
|
|
|
|
|
if (currentBuffer == fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
currentBufferLength = fastByteArrayOutputStream.index;
|
|
|
|
|
if (this.buffersIterator.hasNext()) {
|
|
|
|
|
this.currentBuffer = this.buffersIterator.next();
|
|
|
|
|
if (this.currentBuffer == this.fastByteArrayOutputStream.buffers.getLast()) {
|
|
|
|
|
this.currentBufferLength = this.fastByteArrayOutputStream.index;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBufferLength = currentBuffer.length;
|
|
|
|
|
this.currentBufferLength = this.currentBuffer.length;
|
|
|
|
|
}
|
|
|
|
|
nextIndexInCurrentBuffer = 0;
|
|
|
|
|
this.nextIndexInCurrentBuffer = 0;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
currentBuffer = null;
|
|
|
|
|
this.currentBuffer = null;
|
|
|
|
|
}
|
|
|
|
|
updateMessageDigest(messageDigest, len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|