/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
This class implements an output stream in which the data is
written into a byte array. The buffer automatically grows as data
is written to it.
The data can be retrieved using toByteArray()
and
toString()
.
Closing a ByteArrayOutputStream
has no effect. The methods in this class can be called after the stream has been closed without generating an IOException
.
This is an alternative implementation of the ByteArrayOutputStream
class. The original implementation only allocates 32 bytes at the beginning. As this class is designed for heavy duty it starts at 1024 bytes. In contrast to the original it doesn't reallocate the whole memory block but allocates additional buffers. This way no buffers need to be garbage collected and the contents don't have to be copied to the new buffer. This class is designed to behave exactly like the original. The only exception is the deprecated toString(int) method that has been ignored.
/**
* This class implements an output stream in which the data is
* written into a byte array. The buffer automatically grows as data
* is written to it.
* <p>
* The data can be retrieved using <code>toByteArray()</code> and
* <code>toString()</code>.
* <p>
* Closing a {@code ByteArrayOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
* <p>
* This is an alternative implementation of the {@link java.io.ByteArrayOutputStream}
* class. The original implementation only allocates 32 bytes at the beginning.
* As this class is designed for heavy duty it starts at 1024 bytes. In contrast
* to the original it doesn't reallocate the whole memory block but allocates
* additional buffers. This way no buffers need to be garbage collected and
* the contents don't have to be copied to the new buffer. This class is
* designed to behave exactly like the original. The only exception is the
* deprecated toString(int) method that has been ignored.
*/
public class ByteArrayOutputStream extends OutputStream {
static final int DEFAULT_SIZE = 1024;
A singleton empty byte array. /** A singleton empty byte array. */
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
The list of buffers, which grows and never reduces. /** The list of buffers, which grows and never reduces. */
private final List<byte[]> buffers = new ArrayList<>();
The index of the current buffer. /** The index of the current buffer. */
private int currentBufferIndex;
The total count of bytes in all the filled buffers. /** The total count of bytes in all the filled buffers. */
private int filledBufferSum;
The current buffer. /** The current buffer. */
private byte[] currentBuffer;
The total count of bytes written. /** The total count of bytes written. */
private int count;
Creates a new byte array output stream. The buffer capacity is
initially 1024 bytes, though its size increases if necessary.
/**
* Creates a new byte array output stream. The buffer capacity is
* initially 1024 bytes, though its size increases if necessary.
*/
public ByteArrayOutputStream() {
this(DEFAULT_SIZE);
}
Creates a new byte array output stream, with a buffer capacity of
the specified size, in bytes.
Params: - size – the initial size
Throws: - IllegalArgumentException – if size is negative
/**
* Creates a new byte array output stream, with a buffer capacity of
* the specified size, in bytes.
*
* @param size the initial size
* @throws IllegalArgumentException if size is negative
*/
public ByteArrayOutputStream(final int size) {
if (size < 0) {
throw new IllegalArgumentException(
"Negative initial size: " + size);
}
synchronized (this) {
needNewBuffer(size);
}
}
Makes a new buffer available either by allocating
a new one or re-cycling an existing one.
Params: - newcount – the size of the buffer if one is created
/**
* Makes a new buffer available either by allocating
* a new one or re-cycling an existing one.
*
* @param newcount the size of the buffer if one is created
*/
private void needNewBuffer(final int newcount) {
if (currentBufferIndex < buffers.size() - 1) {
//Recycling old buffer
filledBufferSum += currentBuffer.length;
currentBufferIndex++;
currentBuffer = buffers.get(currentBufferIndex);
} else {
//Creating new buffer
int newBufferSize;
if (currentBuffer == null) {
newBufferSize = newcount;
filledBufferSum = 0;
} else {
newBufferSize = Math.max(
currentBuffer.length << 1,
newcount - filledBufferSum);
filledBufferSum += currentBuffer.length;
}
currentBufferIndex++;
currentBuffer = new byte[newBufferSize];
buffers.add(currentBuffer);
}
}
Write the bytes to byte array.
Params: - b – the bytes to write
- off – The start offset
- len – The number of bytes to write
/**
* Write the bytes to byte array.
* @param b the bytes to write
* @param off The start offset
* @param len The number of bytes to write
*/
@Override
public void write(final byte[] b, final int off, final int len) {
if ((off < 0)
|| (off > b.length)
|| (len < 0)
|| ((off + len) > b.length)
|| ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
synchronized (this) {
final int newcount = count + len;
int remaining = len;
int inBufferPos = count - filledBufferSum;
while (remaining > 0) {
final int part = Math.min(remaining, currentBuffer.length - inBufferPos);
System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
remaining -= part;
if (remaining > 0) {
needNewBuffer(newcount);
inBufferPos = 0;
}
}
count = newcount;
}
}
Write a byte to byte array.
Params: - b – the byte to write
/**
* Write a byte to byte array.
* @param b the byte to write
*/
@Override
public synchronized void write(final int b) {
int inBufferPos = count - filledBufferSum;
if (inBufferPos == currentBuffer.length) {
needNewBuffer(count + 1);
inBufferPos = 0;
}
currentBuffer[inBufferPos] = (byte) b;
count++;
}
Writes the entire contents of the specified input stream to this
byte stream. Bytes from the input stream are read directly into the
internal buffers of this streams.
Params: - in – the input stream to read from
Throws: - IOException – if an I/O error occurs while reading the input stream
Returns: total number of bytes read from the input stream
(and written to this stream) Since: 1.4
/**
* Writes the entire contents of the specified input stream to this
* byte stream. Bytes from the input stream are read directly into the
* internal buffers of this streams.
*
* @param in the input stream to read from
* @return total number of bytes read from the input stream
* (and written to this stream)
* @throws IOException if an I/O error occurs while reading the input stream
* @since 1.4
*/
public synchronized int write(final InputStream in) throws IOException {
int readCount = 0;
int inBufferPos = count - filledBufferSum;
int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
while (n != -1) {
readCount += n;
inBufferPos += n;
count += n;
if (inBufferPos == currentBuffer.length) {
needNewBuffer(currentBuffer.length);
inBufferPos = 0;
}
n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
}
return readCount;
}
Closing a ByteArrayOutputStream
has no effect. The methods in this class can be called after the stream has been closed without generating an IOException
. Throws: - IOException – never (this method should not declare this exception
but it has to now due to backwards compatibility)
/**
* Closing a {@code ByteArrayOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*
* @throws IOException never (this method should not declare this exception
* but it has to now due to backwards compatibility)
*/
@Override
public void close() throws IOException {
//nop
}
Writes the entire contents of this byte stream to the
specified output stream.
Params: - out – the output stream to write to
Throws: - IOException – if an I/O error occurs, such as if the stream is closed
See Also:
/**
* Writes the entire contents of this byte stream to the
* specified output stream.
*
* @param out the output stream to write to
* @throws IOException if an I/O error occurs, such as if the stream is closed
* @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
*/
public synchronized void writeTo(final OutputStream out) throws IOException {
int remaining = count;
for (final byte[] buf : buffers) {
final int c = Math.min(buf.length, remaining);
out.write(buf, 0, c);
remaining -= c;
if (remaining == 0) {
break;
}
}
}
Gets the current contents of this byte stream as a byte array.
The result is independent of this stream.
See Also: Returns: the current contents of this output stream, as a byte array
/**
* Gets the current contents of this byte stream as a byte array.
* The result is independent of this stream.
*
* @return the current contents of this output stream, as a byte array
* @see java.io.ByteArrayOutputStream#toByteArray()
*/
public synchronized byte[] toByteArray() {
int remaining = count;
if (remaining == 0) {
return EMPTY_BYTE_ARRAY;
}
final byte newbuf[] = new byte[remaining];
int pos = 0;
for (final byte[] buf : buffers) {
final int c = Math.min(buf.length, remaining);
System.arraycopy(buf, 0, newbuf, pos, c);
pos += c;
remaining -= c;
if (remaining == 0) {
break;
}
}
return newbuf;
}
}