Skip to content

Commit 6eca4c0

Browse files
author
Stephen Colebourne
committed
IO-97 - ByteArrayOutputStream performance enhancements, from Holger Hoffstatte
git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/io/trunk@491007 13f79535-47bb-0310-9956-ffa450edef68
1 parent f43c85a commit 6eca4c0

3 files changed

Lines changed: 47 additions & 18 deletions

File tree

RELEASE-NOTES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ Enhancements from 1.2
189189
- NullReader
190190
- New reader that emulates a reader of a specified size
191191

192+
- ByteArrayOutputStream [IO-97]
193+
- Performance enhancements
194+
192195

193196
Feedback
194197
--------

src/java/org/apache/commons/io/output/ByteArrayOutputStream.java

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,23 @@
4444
* deprecated toString(int) method that has been ignored.
4545
*
4646
* @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
47+
* @author Holger Hoffstatte
4748
* @version $Id$
4849
*/
4950
public class ByteArrayOutputStream extends OutputStream {
5051

52+
/** A singleton empty byte array. */
53+
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
54+
55+
/** The list of buffers, which grows and never reduces. */
5156
private List buffers = new ArrayList();
57+
/** The index of the current buffer. */
5258
private int currentBufferIndex;
59+
/** The total count of bytes in all the filled buffers. */
5360
private int filledBufferSum;
61+
/** The current buffer. */
5462
private byte[] currentBuffer;
63+
/** The total count of bytes written. */
5564
private int count;
5665

5766
/**
@@ -85,7 +94,7 @@ public ByteArrayOutputStream(int size) {
8594
* @return the buffer
8695
*/
8796
private byte[] getBuffer(int index) {
88-
return (byte[])buffers.get(index);
97+
return (byte[]) buffers.get(index);
8998
}
9099

91100
/**
@@ -123,7 +132,7 @@ private void needNewBuffer(int newcount) {
123132
/**
124133
* @see java.io.OutputStream#write(byte[], int, int)
125134
*/
126-
public synchronized void write(byte[] b, int off, int len) {
135+
public void write(byte[] b, int off, int len) {
127136
if ((off < 0)
128137
|| (off > b.length)
129138
|| (len < 0)
@@ -133,34 +142,40 @@ public synchronized void write(byte[] b, int off, int len) {
133142
} else if (len == 0) {
134143
return;
135144
}
136-
int newcount = count + len;
137-
int remaining = len;
138-
int inBufferPos = count - filledBufferSum;
139-
while (remaining > 0) {
140-
int part = Math.min(remaining, currentBuffer.length - inBufferPos);
141-
System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
142-
remaining -= part;
143-
if (remaining > 0) {
144-
needNewBuffer(newcount);
145-
inBufferPos = 0;
145+
synchronized (this) {
146+
int newcount = count + len;
147+
int remaining = len;
148+
int inBufferPos = count - filledBufferSum;
149+
while (remaining > 0) {
150+
int part = Math.min(remaining, currentBuffer.length - inBufferPos);
151+
System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
152+
remaining -= part;
153+
if (remaining > 0) {
154+
needNewBuffer(newcount);
155+
inBufferPos = 0;
156+
}
146157
}
158+
count = newcount;
147159
}
148-
count = newcount;
149160
}
150161

151162
/**
152-
* Calls the write(byte[]) method.
153-
*
154163
* @see java.io.OutputStream#write(int)
155164
*/
156165
public synchronized void write(int b) {
157-
write(new byte[] {(byte)b}, 0, 1);
166+
int inBufferPos = count - filledBufferSum;
167+
if (inBufferPos == currentBuffer.length) {
168+
needNewBuffer(count + 1);
169+
inBufferPos = 0;
170+
}
171+
currentBuffer[inBufferPos] = (byte) b;
172+
count++;
158173
}
159174

160175
/**
161176
* @see java.io.ByteArrayOutputStream#size()
162177
*/
163-
public int size() {
178+
public synchronized int size() {
164179
return count;
165180
}
166181

@@ -216,8 +231,11 @@ public synchronized void writeTo(OutputStream out) throws IOException {
216231
*/
217232
public synchronized byte[] toByteArray() {
218233
int remaining = count;
234+
if (remaining == 0) {
235+
return EMPTY_BYTE_ARRAY;
236+
}
237+
byte newbuf[] = new byte[remaining];
219238
int pos = 0;
220-
byte newbuf[] = new byte[count];
221239
for (int i = 0; i < buffers.size(); i++) {
222240
byte[] buf = getBuffer(i);
223241
int c = Math.min(buf.length, remaining);

src/test/org/apache/commons/io/output/ByteArrayOutputStreamTestCase.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ public void testStream() throws Exception {
104104

105105
//First three writes
106106
written = writeData(baout, ref, new int[] {4, 10, 22});
107+
assertEquals(36, written);
107108
checkStreams(baout, ref);
108109

109110
//Another two writes to see if there are any bad effects after toByteArray()
110111
written = writeData(baout, ref, new int[] {20, 12});
112+
assertEquals(32, written);
111113
checkStreams(baout, ref);
112114

113115
//Now reset the streams
@@ -116,6 +118,7 @@ public void testStream() throws Exception {
116118

117119
//Test again to see if reset() had any bad effects
118120
written = writeData(baout, ref, new int[] {5, 47, 33, 60, 1, 0, 8});
121+
assertEquals(155, written);
119122
checkStreams(baout, ref);
120123

121124
//Write the commons Byte[]OutputStream to a java.io.Byte[]OutputStream
@@ -130,6 +133,11 @@ public void testStream() throws Exception {
130133
String baoutString = baout.toString("ASCII");
131134
String refString = ref.toString("ASCII");
132135
assertEquals("ASCII decoded String must be equal", refString, baoutString);
136+
137+
//Make sure that empty ByteArrayOutputStreams really don't create garbage
138+
//on toByteArray()
139+
assertSame(new ByteArrayOutputStream().toByteArray(),
140+
new ByteArrayOutputStream().toByteArray());
133141
}
134142
}
135143

0 commit comments

Comments
 (0)